Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67c08b40ca |
-287
@@ -1,287 +0,0 @@
|
|||||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
|
||||||
root = true
|
|
||||||
|
|
||||||
# C# files
|
|
||||||
[*.cs]
|
|
||||||
# IDE0250: Prefer make struct 'readonly'
|
|
||||||
dotnet_diagnostic.IDE0250.severity = warning
|
|
||||||
|
|
||||||
# IDE0060: Remove unused parameters (Good cleanup pairing)
|
|
||||||
dotnet_diagnostic.IDE0060.severity = warning
|
|
||||||
|
|
||||||
# CA1852: Seal internal types (Available in modern .NET)
|
|
||||||
dotnet_diagnostic.CA1852.severity = warning
|
|
||||||
|
|
||||||
# MA0018: Add sealed modifier to types that are never inherited
|
|
||||||
dotnet_diagnostic.MA0018.severity = warning
|
|
||||||
|
|
||||||
# Enforce that classes should be sealed
|
|
||||||
dotnet_diagnostic.MA0053.severity = warning
|
|
||||||
|
|
||||||
# CRITICAL: Force the analyzer to also flag PUBLIC classes, not just internal ones
|
|
||||||
meziantou_analyzer.MA0053.public_class_should_be_sealed = true
|
|
||||||
MA0053.public_class_should_be_sealed = true
|
|
||||||
|
|
||||||
# Keep the rule active as a warning by default
|
|
||||||
dotnet_diagnostic.MA0048.severity = warning
|
|
||||||
|
|
||||||
# Specific exclusions for Meziantou.Analyzer MA0048
|
|
||||||
# Disable the rule for enums
|
|
||||||
meziantou_analyzer.MA0048.exclude_enums = true
|
|
||||||
|
|
||||||
# Disable the rule for records
|
|
||||||
meziantou_analyzer.MA0048.exclude_records = true
|
|
||||||
|
|
||||||
#EXCLUDE specific files that are meant to hold grouped enums/records
|
|
||||||
dotnet_diagnostic.MA0048.severity = warning
|
|
||||||
|
|
||||||
# Disable the requirement to specify ConfigureAwait(false)
|
|
||||||
dotnet_diagnostic.MA0004.severity = none
|
|
||||||
|
|
||||||
# ALTERNATIVE: Exclude any file ending with 'Enums.cs' or 'Records.cs'
|
|
||||||
# (e.g., BillingEnums.cs, CustomerRecords.cs)
|
|
||||||
[**/*{Enums,Records}.cs]
|
|
||||||
dotnet_diagnostic.MA0048.severity = none
|
|
||||||
|
|
||||||
#### Core EditorConfig Options ####
|
|
||||||
|
|
||||||
# Indentation and spacing
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
tab_width = 4
|
|
||||||
|
|
||||||
# New line preferences
|
|
||||||
end_of_line = crlf
|
|
||||||
insert_final_newline = false
|
|
||||||
|
|
||||||
#### .NET Code Actions ####
|
|
||||||
|
|
||||||
# Type members
|
|
||||||
dotnet_hide_advanced_members = false
|
|
||||||
dotnet_member_insertion_location = with_other_members_of_the_same_kind
|
|
||||||
dotnet_property_generation_behavior = prefer_throwing_properties
|
|
||||||
|
|
||||||
# Symbol search
|
|
||||||
dotnet_search_reference_assemblies = true
|
|
||||||
|
|
||||||
#### .NET Coding Conventions ####
|
|
||||||
|
|
||||||
# Organize usings
|
|
||||||
dotnet_separate_import_directive_groups = false
|
|
||||||
dotnet_sort_system_directives_first = false
|
|
||||||
file_header_template = unset
|
|
||||||
|
|
||||||
# this. and Me. preferences
|
|
||||||
dotnet_style_qualification_for_event = false
|
|
||||||
dotnet_style_qualification_for_field = false
|
|
||||||
dotnet_style_qualification_for_method = false
|
|
||||||
dotnet_style_qualification_for_property = false
|
|
||||||
|
|
||||||
# Language keywords vs BCL types preferences
|
|
||||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
|
||||||
dotnet_style_predefined_type_for_member_access = true
|
|
||||||
|
|
||||||
# Parentheses preferences
|
|
||||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
|
||||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
|
||||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
|
|
||||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
|
||||||
|
|
||||||
# Modifier preferences
|
|
||||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members
|
|
||||||
|
|
||||||
# Expression-level preferences
|
|
||||||
dotnet_prefer_system_hash_code = true
|
|
||||||
dotnet_style_coalesce_expression = true
|
|
||||||
dotnet_style_collection_initializer = true
|
|
||||||
dotnet_style_explicit_tuple_names = true
|
|
||||||
dotnet_style_namespace_match_folder = true
|
|
||||||
dotnet_style_null_propagation = true
|
|
||||||
dotnet_style_object_initializer = true
|
|
||||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
|
||||||
dotnet_style_prefer_auto_properties = true
|
|
||||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
|
||||||
dotnet_style_prefer_compound_assignment = true
|
|
||||||
dotnet_style_prefer_conditional_expression_over_assignment = true
|
|
||||||
dotnet_style_prefer_conditional_expression_over_return = true
|
|
||||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
|
|
||||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true
|
|
||||||
dotnet_style_prefer_inferred_tuple_names = true
|
|
||||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
|
||||||
dotnet_style_prefer_non_hidden_explicit_cast_in_source = true
|
|
||||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
|
||||||
dotnet_style_prefer_simplified_interpolation = true
|
|
||||||
|
|
||||||
# Field preferences
|
|
||||||
dotnet_style_readonly_field = true
|
|
||||||
|
|
||||||
# Parameter preferences
|
|
||||||
dotnet_code_quality_unused_parameters = all
|
|
||||||
|
|
||||||
# Suppression preferences
|
|
||||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
|
||||||
|
|
||||||
# New line preferences
|
|
||||||
dotnet_style_allow_multiple_blank_lines_experimental = true
|
|
||||||
dotnet_style_allow_statement_immediately_after_block_experimental = true
|
|
||||||
|
|
||||||
#### C# Coding Conventions ####
|
|
||||||
|
|
||||||
# var preferences
|
|
||||||
csharp_style_var_elsewhere = false
|
|
||||||
csharp_style_var_for_built_in_types = false
|
|
||||||
csharp_style_var_when_type_is_apparent = false
|
|
||||||
|
|
||||||
# Expression-bodied members
|
|
||||||
csharp_style_expression_bodied_accessors = true
|
|
||||||
csharp_style_expression_bodied_constructors = false
|
|
||||||
csharp_style_expression_bodied_indexers = true
|
|
||||||
csharp_style_expression_bodied_lambdas = true
|
|
||||||
csharp_style_expression_bodied_local_functions = true
|
|
||||||
csharp_style_expression_bodied_methods = false
|
|
||||||
csharp_style_expression_bodied_operators = false
|
|
||||||
csharp_style_expression_bodied_properties = true
|
|
||||||
|
|
||||||
# Pattern matching preferences
|
|
||||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
|
||||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
|
||||||
csharp_style_prefer_extended_property_pattern = true
|
|
||||||
csharp_style_prefer_not_pattern = true
|
|
||||||
csharp_style_prefer_pattern_matching = true
|
|
||||||
csharp_style_prefer_switch_expression = true
|
|
||||||
|
|
||||||
# Null-checking preferences
|
|
||||||
csharp_style_conditional_delegate_call = true
|
|
||||||
|
|
||||||
# Modifier preferences
|
|
||||||
csharp_prefer_static_anonymous_function = true
|
|
||||||
csharp_prefer_static_local_function = true
|
|
||||||
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
|
|
||||||
csharp_style_prefer_readonly_struct = true
|
|
||||||
csharp_style_prefer_readonly_struct_member = true
|
|
||||||
|
|
||||||
# Code-block preferences
|
|
||||||
csharp_prefer_braces = true
|
|
||||||
csharp_prefer_simple_using_statement = true
|
|
||||||
csharp_prefer_system_threading_lock = true
|
|
||||||
csharp_style_namespace_declarations = block_scoped
|
|
||||||
csharp_style_prefer_method_group_conversion = true
|
|
||||||
csharp_style_prefer_primary_constructors = true
|
|
||||||
csharp_style_prefer_simple_property_accessors = true
|
|
||||||
csharp_style_prefer_top_level_statements = true
|
|
||||||
|
|
||||||
# Expression-level preferences
|
|
||||||
csharp_prefer_simple_default_expression = true
|
|
||||||
csharp_style_deconstructed_variable_declaration = true
|
|
||||||
csharp_style_implicit_object_creation_when_type_is_apparent = true
|
|
||||||
csharp_style_inlined_variable_declaration = true
|
|
||||||
csharp_style_prefer_implicitly_typed_lambda_expression = true
|
|
||||||
csharp_style_prefer_index_operator = true
|
|
||||||
csharp_style_prefer_local_over_anonymous_function = true
|
|
||||||
csharp_style_prefer_null_check_over_type_check = true
|
|
||||||
csharp_style_prefer_range_operator = true
|
|
||||||
csharp_style_prefer_tuple_swap = true
|
|
||||||
csharp_style_prefer_unbound_generic_type_in_nameof = true
|
|
||||||
csharp_style_prefer_utf8_string_literals = true
|
|
||||||
csharp_style_throw_expression = true
|
|
||||||
csharp_style_unused_value_assignment_preference = discard_variable
|
|
||||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
|
||||||
|
|
||||||
# 'using' directive preferences
|
|
||||||
csharp_using_directive_placement = outside_namespace
|
|
||||||
|
|
||||||
# New line preferences
|
|
||||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
|
|
||||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
|
|
||||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
|
|
||||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
|
|
||||||
csharp_style_allow_embedded_statements_on_same_line_experimental = true
|
|
||||||
|
|
||||||
#### C# Formatting Rules ####
|
|
||||||
|
|
||||||
# New line preferences
|
|
||||||
csharp_new_line_before_catch = true
|
|
||||||
csharp_new_line_before_else = true
|
|
||||||
csharp_new_line_before_finally = true
|
|
||||||
csharp_new_line_before_members_in_anonymous_types = true
|
|
||||||
csharp_new_line_before_members_in_object_initializers = true
|
|
||||||
csharp_new_line_before_open_brace = all
|
|
||||||
csharp_new_line_between_query_expression_clauses = true
|
|
||||||
|
|
||||||
# Indentation preferences
|
|
||||||
csharp_indent_block_contents = true
|
|
||||||
csharp_indent_braces = false
|
|
||||||
csharp_indent_case_contents = true
|
|
||||||
csharp_indent_case_contents_when_block = true
|
|
||||||
csharp_indent_labels = one_less_than_current
|
|
||||||
csharp_indent_switch_labels = true
|
|
||||||
|
|
||||||
# Space preferences
|
|
||||||
csharp_space_after_cast = false
|
|
||||||
csharp_space_after_colon_in_inheritance_clause = true
|
|
||||||
csharp_space_after_comma = true
|
|
||||||
csharp_space_after_dot = false
|
|
||||||
csharp_space_after_keywords_in_control_flow_statements = true
|
|
||||||
csharp_space_after_semicolon_in_for_statement = true
|
|
||||||
csharp_space_around_binary_operators = before_and_after
|
|
||||||
csharp_space_around_declaration_statements = false
|
|
||||||
csharp_space_before_colon_in_inheritance_clause = true
|
|
||||||
csharp_space_before_comma = false
|
|
||||||
csharp_space_before_dot = false
|
|
||||||
csharp_space_before_open_square_brackets = false
|
|
||||||
csharp_space_before_semicolon_in_for_statement = false
|
|
||||||
csharp_space_between_empty_square_brackets = false
|
|
||||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
|
||||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
|
||||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
|
||||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
|
||||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
|
||||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
|
||||||
csharp_space_between_parentheses = false
|
|
||||||
csharp_space_between_square_brackets = false
|
|
||||||
|
|
||||||
# Wrapping preferences
|
|
||||||
csharp_preserve_single_line_blocks = true
|
|
||||||
csharp_preserve_single_line_statements = true
|
|
||||||
|
|
||||||
#### Naming styles ####
|
|
||||||
|
|
||||||
# Naming rules
|
|
||||||
|
|
||||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
|
||||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
|
||||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
|
||||||
|
|
||||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
|
||||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
|
||||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
|
||||||
|
|
||||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
|
||||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
|
||||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
|
||||||
|
|
||||||
# Symbol specifications
|
|
||||||
|
|
||||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
|
||||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
|
||||||
dotnet_naming_symbols.interface.required_modifiers =
|
|
||||||
|
|
||||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
|
||||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
|
||||||
dotnet_naming_symbols.types.required_modifiers =
|
|
||||||
|
|
||||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
|
||||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
|
||||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
|
||||||
|
|
||||||
# Naming styles
|
|
||||||
|
|
||||||
dotnet_naming_style.pascal_case.required_prefix =
|
|
||||||
dotnet_naming_style.pascal_case.required_suffix =
|
|
||||||
dotnet_naming_style.pascal_case.word_separator =
|
|
||||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
|
||||||
|
|
||||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
|
||||||
dotnet_naming_style.begins_with_i.required_suffix =
|
|
||||||
dotnet_naming_style.begins_with_i.word_separator =
|
|
||||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
*.env
|
*.env
|
||||||
*.env.json
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
@@ -37,7 +36,6 @@ obj/x64/
|
|||||||
[Rr]eleases/x86/
|
[Rr]eleases/x86/
|
||||||
bin/x86/
|
bin/x86/
|
||||||
obj/x86/
|
obj/x86/
|
||||||
bin/
|
|
||||||
|
|
||||||
[Ww][Ii][Nn]32/
|
[Ww][Ii][Nn]32/
|
||||||
[Aa][Rr][Mm]/
|
[Aa][Rr][Mm]/
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
PROPRIETARY LICENSE
|
|
||||||
|
|
||||||
Copyright (c) 2026 IFRI TECHNOLOGIES (PTY) Ltd. All rights reserved.
|
|
||||||
|
|
||||||
This software and its associated documentation (the "Software") are the
|
|
||||||
proprietary property of IFRI TECHNOLOGIES (PTY) Ltd.
|
|
||||||
|
|
||||||
The Software is provided for internal use only. Unauthorized copying,
|
|
||||||
distribution, modification, or use of this file via any medium is
|
|
||||||
strictly prohibited.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
||||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using PostFundManagement.Infrastructure.Database;
|
|
||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Extensions;
|
|
||||||
|
|
||||||
public static class Postgres
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddDataProtectionDatabase(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
|
||||||
|
|
||||||
services.AddPooledDbContextFactory<DataProtectionDbContext>(options =>
|
|
||||||
options.UseNpgsql(connectionString));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddApplicationDbContext(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName)
|
|
||||||
?? throw new InvalidOperationException($"Connection string '{DatabaseConfigName}' was not found in configuration.");
|
|
||||||
|
|
||||||
services.AddPooledDbContextFactory<ApplicationDbContext>(builder => builder.UseNpgsql(connectionString));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Api.Configuration;
|
|
||||||
using PostFundManagement.Domain.Extensions;
|
|
||||||
using PostFundManagement.Domain.Sdk;
|
|
||||||
using PostFundManagement.Domain.Services;
|
|
||||||
using PostFundManagement.Infrastructure.Database;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Api.Extensions;
|
|
||||||
|
|
||||||
public static class Security
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddSecurityApiSdk(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var configSection = configuration.GetSection(nameof(SecurityClientSettings));
|
|
||||||
|
|
||||||
var authOptions = new SecurityClientSettings();
|
|
||||||
configSection.Bind(authOptions);
|
|
||||||
|
|
||||||
services.Configure<SecurityClientSettings>(configSection);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(authOptions.Authority))
|
|
||||||
return services;
|
|
||||||
|
|
||||||
if (!authOptions.Authority.EndsWith("/", StringComparison.Ordinal)) authOptions.Authority += "/";
|
|
||||||
|
|
||||||
services.AddRefitClient<ISecurityConnectApi>()
|
|
||||||
.ConfigureHttpClient(config =>
|
|
||||||
{
|
|
||||||
config.BaseAddress = new Uri(authOptions.Authority);
|
|
||||||
config.Timeout = TimeSpan.FromSeconds(15);
|
|
||||||
})
|
|
||||||
.AddStandardResilienceHandler(options =>
|
|
||||||
{
|
|
||||||
options.Retry.MaxRetryAttempts = 3;
|
|
||||||
options.Retry.Delay = TimeSpan.FromSeconds(1);
|
|
||||||
options.Retry.BackoffType = Polly.DelayBackoffType.Exponential;
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddScoped<TokenService>();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddWebSecurity(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddAuthorization();
|
|
||||||
|
|
||||||
var certString = configuration["DataProtection:Certificate"] ?? configuration["DataProtection__Certificate"];
|
|
||||||
var certPassword = configuration["DataProtection:Password"] ?? configuration["DataProtection__Password"];
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(certString))
|
|
||||||
throw new InvalidOperationException("Data Protection Certificate configuration is missing.");
|
|
||||||
|
|
||||||
var certificate = X509CertificateLoader.LoadPkcs12(Convert.FromBase64String(certString), certPassword);
|
|
||||||
|
|
||||||
services.AddDataProtection().PersistKeysToDbContext<DataProtectionDbContext>()
|
|
||||||
.ProtectKeysWithCertificate(certificate)
|
|
||||||
.SetApplicationName("LiteCharmsApp");
|
|
||||||
|
|
||||||
services.Configure<DataProtectionOptions>(options => options.ApplicationDiscriminator = "LiteCharmsApp");
|
|
||||||
|
|
||||||
services.ConfigureCookieOidcSameSiteSupport();
|
|
||||||
|
|
||||||
var configSection = configuration.GetSection(nameof(SecuritySettings));
|
|
||||||
|
|
||||||
var authOptions = new SecuritySettings();
|
|
||||||
configSection.Bind(authOptions);
|
|
||||||
|
|
||||||
services.Configure<SecuritySettings>(configSection);
|
|
||||||
|
|
||||||
services.AddAuthentication(options =>
|
|
||||||
{
|
|
||||||
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
||||||
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
|
|
||||||
})
|
|
||||||
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
|
|
||||||
{
|
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
|
||||||
options.Cookie.SameSite = SameSiteMode.Lax;
|
|
||||||
options.Cookie.Name = "LiteCharmsApp.Session";
|
|
||||||
})
|
|
||||||
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
|
|
||||||
{
|
|
||||||
options.Authority = authOptions.Authority;
|
|
||||||
options.ClientId = authOptions.ClientId;
|
|
||||||
options.ClientSecret = authOptions.ClientSecret;
|
|
||||||
options.ResponseType = "code";
|
|
||||||
|
|
||||||
options.SaveTokens = true;
|
|
||||||
options.GetClaimsFromUserInfoEndpoint = true;
|
|
||||||
options.CorrelationCookie.SecurePolicy = CookieSecurePolicy.Always;
|
|
||||||
options.CorrelationCookie.SameSite = SameSiteMode.None;
|
|
||||||
|
|
||||||
options.NonceCookie.SecurePolicy = CookieSecurePolicy.Always;
|
|
||||||
options.NonceCookie.SameSite = SameSiteMode.None;
|
|
||||||
|
|
||||||
options.ForwardSignOut = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
||||||
|
|
||||||
options.Scope.Clear();
|
|
||||||
options.Scope.Add("openid");
|
|
||||||
options.Scope.Add("profile");
|
|
||||||
options.Scope.Add("email");
|
|
||||||
|
|
||||||
options.Events = new OpenIdConnectEvents
|
|
||||||
{
|
|
||||||
OnRedirectToIdentityProviderForSignOut = context =>
|
|
||||||
{
|
|
||||||
var idToken = context.ProtocolMessage.IdTokenHint;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(idToken))
|
|
||||||
{
|
|
||||||
var tokens = context.Properties.GetTokens();
|
|
||||||
var idTokenItem = tokens.FirstOrDefault(t => string.Equals(t.Name, "id_token", StringComparison.Ordinal));
|
|
||||||
|
|
||||||
if (idTokenItem != null) context.ProtocolMessage.IdTokenHint = idTokenItem.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddCascadingAuthenticationState();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +1,13 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<UserSecretsId>59a05094-4ac2-472d-af04-3407e909432d</UserSecretsId>
|
|
||||||
<AssemblyOriginatorKeyFile>..\PostFundManagement.snk</AssemblyOriginatorKeyFile>
|
|
||||||
<SignAssembly>True</SignAssembly>
|
|
||||||
<PackageLicenseFile>..\LICENSE</PackageLicenseFile>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Security (IODC)-->
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
|
||||||
<PackageReference Include="IdentityModel.AspNetCore.OAuth2introspection" Version="6.2.0" />
|
|
||||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
|
||||||
<PackageReference Include="IdentityModel" Version="6.2.0" />
|
|
||||||
<PackageReference Include="KubernetesClient" Version="19.0.2" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.11" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.11" />
|
|
||||||
|
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Health Checks -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
|
|
||||||
|
|
||||||
<Using Include="Microsoft.Extensions.Diagnostics.HealthChecks" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.Diagnostics.HealthChecks" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- API Versioning -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AccessTokenClient.Extensions" Version="5.1.0" />
|
|
||||||
<PackageReference Include="Asp.Versioning.Abstractions" Version="10.2.1" />
|
|
||||||
<PackageReference Include="Asp.Versioning.Http" Version="10.2.2" />
|
|
||||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="10.2.1" />
|
|
||||||
|
|
||||||
<Using Include="Asp.Versioning" />
|
|
||||||
<Using Include="Asp.Versioning.Builder" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- API Documentation -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.11" />
|
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.17.1" />
|
|
||||||
|
|
||||||
<Using Include="Scalar.AspNetCore" />
|
|
||||||
<Using Include="Microsoft.OpenApi" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.OpenApi" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- file nesting -->
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectCapability Include="ConfigurableFileNesting" />
|
|
||||||
<ProjectCapability Include="ConfigurableFileNestingFeatureEnabled" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- CQRS -->
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.2">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
|
|
||||||
<Using Include="FluentResults" />
|
|
||||||
<Using Include="Mediator" />
|
|
||||||
<Using Include="Quartz" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<!-- Shared Global Usings -->
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="System.Web" />
|
|
||||||
<Using Include="System.Diagnostics" />
|
|
||||||
<Using Include="System.Reflection" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.Mvc" />
|
|
||||||
<Using Include="Microsoft.EntityFrameworkCore" />
|
|
||||||
<Using Include="System.ComponentModel.DataAnnotations" />
|
|
||||||
<Using Include="Microsoft.Extensions.DependencyInjection.Extensions" />
|
|
||||||
<Using Include="System.Security.Cryptography.X509Certificates" />
|
|
||||||
<Using Include="Microsoft.AspNetCore.DataProtection" />
|
|
||||||
<Using Include="Refit" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\PostFundManagement.Domain\PostFundManagement.Domain.csproj" />
|
|
||||||
<ProjectReference Include="..\PostFundManagement.Infrastructure\PostFundManagement.Infrastructure.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@PostFundManagement.Api_HostAddress = http://localhost:5112
|
||||||
|
|
||||||
|
GET {{PostFundManagement.Api_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
@@ -1,100 +1,41 @@
|
|||||||
using PostFundManagement.Api.Extensions;
|
|
||||||
using PostFundManagement.Domain.Extensions;
|
|
||||||
using PostFundManagement.Domain.Health;
|
|
||||||
using PostFundManagement.Domain.Mediator;
|
|
||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddEndpointsApiExplorer()
|
// Add services to the container.
|
||||||
.AddApiVersioning(options => options.ApiVersionReader = new HeaderApiVersionReader());
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||||
|
builder.Services.AddOpenApi();
|
||||||
builder.Services.AddApiVersioning().AddApiExplorer();
|
|
||||||
|
|
||||||
builder.Services.AddEndpoints(Assembly.GetExecutingAssembly());
|
|
||||||
builder.Services.AddApiServices(builder.Configuration);
|
|
||||||
|
|
||||||
builder.Services.AddMediator();
|
|
||||||
builder.Services.AddSecurityApiSdk(builder.Configuration);
|
|
||||||
builder.Services.AddWebSecurity(builder.Configuration);
|
|
||||||
|
|
||||||
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TelemetryPipelineBehavior<,>));
|
|
||||||
builder.Services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingPipelineBehavior<,>));
|
|
||||||
|
|
||||||
builder.Services.AddQuartzScheduler(DefaultSchedulerName, builder.Configuration);
|
|
||||||
|
|
||||||
builder.Services.AddEmailServices(builder.Configuration);
|
|
||||||
|
|
||||||
builder.Services.AddHttpClient();
|
|
||||||
builder.Services.AddHashServices(builder.Configuration);
|
|
||||||
builder.Services.AddDataProtectionDatabase(builder.Configuration);
|
|
||||||
builder.Services.AddApplicationDbContext(builder.Configuration);
|
|
||||||
|
|
||||||
builder.Services.AddHealthChecks()
|
|
||||||
.AddCheck<QuartzHealthCheck>("QuartzScheduler")
|
|
||||||
.AddCheck<PostgresHealthCheck>("PostgresDatabase")
|
|
||||||
.AddCheck("Self", () => HealthCheckResult.Healthy());
|
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
var schedulerFactory = app.Services.GetRequiredService<ISchedulerFactory>();
|
// Configure the HTTP request pipeline.
|
||||||
var scheduler = await schedulerFactory.GetScheduler(DefaultSchedulerName);
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.MapOpenApi();
|
||||||
|
}
|
||||||
|
|
||||||
if (!scheduler!.IsStarted)
|
|
||||||
await scheduler.Start();
|
|
||||||
|
|
||||||
app.UseHsts();
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseRouting();
|
var summaries = new[]
|
||||||
app.UseAuthentication();
|
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
ApiVersionSet versionSet = app.NewApiVersionSet("v1")
|
|
||||||
.HasApiVersion(new ApiVersion(1))
|
|
||||||
.HasApiVersion(new ApiVersion(2))
|
|
||||||
.ReportApiVersions()
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var versionGroups = new Dictionary<int, RouteGroupBuilder>
|
|
||||||
{
|
{
|
||||||
{ 1, app.MapGroup("v{version:apiVersion}").WithApiVersionSet(versionSet) }
|
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||||
};
|
};
|
||||||
|
|
||||||
app.MapEndpoints(versionGroups);
|
app.MapGet("/weatherforecast", () =>
|
||||||
|
|
||||||
app.UseHealthChecks("/health", new HealthCheckOptions
|
|
||||||
{
|
{
|
||||||
Predicate = _ => true,
|
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||||
AllowCachingResponses = true,
|
new WeatherForecast
|
||||||
ResponseWriter = HealthChecks.UI.Client.UIResponseWriter.WriteHealthCheckUIResponse
|
(
|
||||||
});
|
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||||
|
Random.Shared.Next(-20, 55),
|
||||||
|
summaries[Random.Shared.Next(summaries.Length)]
|
||||||
|
))
|
||||||
|
.ToArray();
|
||||||
|
return forecast;
|
||||||
|
})
|
||||||
|
.WithName("GetWeatherForecast");
|
||||||
|
|
||||||
app.MapHealthChecksUI(options => { options.UIPath = "/healthui"; });
|
app.Run();
|
||||||
app.UseHealthChecks("/ready");
|
|
||||||
|
|
||||||
app.MapOpenApi();
|
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||||
|
{
|
||||||
var apiVersions = app.DescribeApiVersions()
|
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||||
.OrderByDescending(o => o.ApiVersion.MajorVersion)
|
}
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var description in app.DescribeApiVersions().OrderByDescending(o => o.ApiVersion.MajorVersion))
|
|
||||||
app.MapScalarApiReference($"/openapi/{description.GroupName}", (options, context) =>
|
|
||||||
{
|
|
||||||
options.AddServer(new ScalarServer($"https://{context.Request.Host}"));
|
|
||||||
options.WithOpenApiRoutePattern($"/openapi/{description.GroupName}.json");
|
|
||||||
options.WithTheme(ScalarTheme.DeepSpace);
|
|
||||||
options.Agent = new ScalarAgentOptions { Disabled = true };
|
|
||||||
options.Authentication = new ScalarAuthenticationOptions { PreferredSecuritySchemes = ["Bearer"] };
|
|
||||||
});
|
|
||||||
|
|
||||||
var latestVersionGroup = apiVersions.FirstOrDefault()?.GroupName ?? "v1";
|
|
||||||
|
|
||||||
app.MapGet("/", () => Results.Redirect($"/openapi/{latestVersionGroup}"))
|
|
||||||
.ExcludeFromDescription();
|
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,4 @@
|
|||||||
{
|
{
|
||||||
"GeneralS3Settings": {
|
|
||||||
"ServiceUrl": "http://192.168.1.177:30900",
|
|
||||||
"Region": "garage",
|
|
||||||
"BucketName": "pfm.general",
|
|
||||||
"CdnBaseUrl": "https://pfm.general.cdn.khongisa.co.za"
|
|
||||||
},
|
|
||||||
"Monitoring": {
|
|
||||||
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
|
|
||||||
"ServiceName": "MidrandBooks.DEV"
|
|
||||||
},
|
|
||||||
"SecuritySettings": {
|
|
||||||
"Authority": "https://sts.security.khongisa.co.za",
|
|
||||||
"Audience": "pfm-api-dev"
|
|
||||||
},
|
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
@@ -20,4 +6,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
@hostname = api.example.com
|
|
||||||
@port = 8080
|
|
||||||
@host = {{hostname}}:{{port}}
|
|
||||||
@contentType = application/json
|
|
||||||
|
|
||||||
###
|
|
||||||
# @prompt username
|
|
||||||
# @prompt refCode Your reference code display on webpage
|
|
||||||
# @prompt otp Your one-time password in your mailbox
|
|
||||||
POST https://{{host}}/verify-otp/{{refCode}} HTTP/1.1
|
|
||||||
Content-Type: {{contentType}}
|
|
||||||
|
|
||||||
{
|
|
||||||
"username": "{{username}}",
|
|
||||||
"otp": "{{otp}}"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
### Token Request
|
|
||||||
POST https://{{authority}}/connect/token
|
|
||||||
Content-Type: application/x-www-form-urlencoded
|
|
||||||
Accept-Encoding: identity
|
|
||||||
|
|
||||||
grant_type={{grantType}}&client_id={{clientId}}&client_secret={{clientSecret}}&scope={{scope}}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Timezones;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public abstract class EventBase
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; } = Guid.CreateVersion7();
|
|
||||||
|
|
||||||
public DateTimeOffset EnqueueAt { get; set; } = (DateTimeOffset)SouthAfricanTimeZone.UtcNow();
|
|
||||||
|
|
||||||
public string CorrelationId { get; set; } = Guid.CreateVersion7().ToString();
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public interface IEndpoint
|
|
||||||
{
|
|
||||||
void Map(IEndpointRouteBuilder builder);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public interface IEvent : INotification
|
|
||||||
{
|
|
||||||
Guid Id { get; set; }
|
|
||||||
|
|
||||||
string Name { get; set; }
|
|
||||||
|
|
||||||
DateTimeOffset EnqueueAt { get; set; }
|
|
||||||
|
|
||||||
string CorrelationId { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public interface IJobOrchestrator
|
|
||||||
{
|
|
||||||
ValueTask SendAsync<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
|
|
||||||
where TNotification : IEvent;
|
|
||||||
|
|
||||||
ValueTask ScheduleAsync<TNotification>(TNotification notification, string cronExpression, CancellationToken cancellationToken = default)
|
|
||||||
where TNotification : IEvent;
|
|
||||||
|
|
||||||
ValueTask<bool> InterruptAsync(string eventName, string? correlationId = null, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public interface IS3Service
|
|
||||||
{
|
|
||||||
Task<Result<string>> UploadFileAsync(string fileName, Stream fileStream, string contentType, CancellationToken cancellationToken = default);
|
|
||||||
Task<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public interface IService;
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Services;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Abstractions;
|
|
||||||
|
|
||||||
public abstract class S3ServiceBase(IAmazonS3 amazonS3)
|
|
||||||
{
|
|
||||||
protected readonly IAmazonS3 Client = amazonS3;
|
|
||||||
|
|
||||||
protected abstract string BucketName { get; }
|
|
||||||
protected abstract string CdnBaseUrl { get; }
|
|
||||||
|
|
||||||
public virtual async Task<Result<string>> UploadFileAsync(string fileName, Stream fileStream, string contentType, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(BucketName))
|
|
||||||
return Result.Fail<string>("Bucket name is not configured.");
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(CdnBaseUrl))
|
|
||||||
return Result.Fail<string>("CDN base URL is not configured.");
|
|
||||||
|
|
||||||
using var stream = new MemoryStream();
|
|
||||||
|
|
||||||
await fileStream.CopyToAsync(stream, cancellationToken);
|
|
||||||
await fileStream.DisposeAsync();
|
|
||||||
|
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
var fileHash = HashService.StreamToSha256Hash(stream);
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(fileHash))
|
|
||||||
return Result.Fail<string>("Failed to compute file hash.");
|
|
||||||
|
|
||||||
var fileKey = $"{fileHash.ToLower(CultureInfo.InvariantCulture)}{Path.GetExtension(fileName)}";
|
|
||||||
|
|
||||||
var putRequest = new PutObjectRequest
|
|
||||||
{
|
|
||||||
BucketName = BucketName,
|
|
||||||
Key = fileKey,
|
|
||||||
InputStream = stream,
|
|
||||||
ContentType = contentType,
|
|
||||||
UseChunkEncoding = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
var response = await Client.PutObjectAsync(putRequest, cancellationToken);
|
|
||||||
|
|
||||||
return response.HttpStatusCode != System.Net.HttpStatusCode.OK
|
|
||||||
? Result.Fail<string>($"Failed to upload {fileName} to S3.")
|
|
||||||
: Result.Ok($"{CdnBaseUrl}/{fileKey}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return Result.Fail<string>(new Error($"Error uploading {fileName} to S3: {ex.Message}").CausedBy(ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual async Task<Result> DeleteFileAsync(string fileKey, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(BucketName))
|
|
||||||
return Result.Fail("Bucket name is not configured.");
|
|
||||||
|
|
||||||
var deleteRequest = new DeleteObjectRequest
|
|
||||||
{
|
|
||||||
BucketName = BucketName,
|
|
||||||
Key = fileKey
|
|
||||||
};
|
|
||||||
|
|
||||||
var response = await Client.DeleteObjectAsync(deleteRequest, cancellationToken);
|
|
||||||
|
|
||||||
return response.HttpStatusCode != System.Net.HttpStatusCode.NoContent
|
|
||||||
? Result.Fail($"Failed to delete {fileKey} from S3.")
|
|
||||||
: Result.Ok();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return Result.Fail(new Error($"Error deleting {fileKey} from S3: {ex.Message}").CausedBy(ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
|
||||||
public sealed class ApiVersionTargetAttribute(int majorVersion) : Attribute
|
|
||||||
{
|
|
||||||
public int MajorVersion { get; } = majorVersion;
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api.Configuration;
|
|
||||||
|
|
||||||
public sealed class SecurityClientSettings
|
|
||||||
{
|
|
||||||
public string? Authority { get; set; }
|
|
||||||
|
|
||||||
public string? GrantType { get; set; }
|
|
||||||
|
|
||||||
public string? ClientId { get; set; }
|
|
||||||
|
|
||||||
public string? ClientSecret { get; set; }
|
|
||||||
|
|
||||||
public string? Scope { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api.Configuration;
|
|
||||||
|
|
||||||
public sealed class SecuritySettings
|
|
||||||
{
|
|
||||||
public string? Authority { get; set; }
|
|
||||||
|
|
||||||
public string? ClientId { get; set; }
|
|
||||||
|
|
||||||
public string? ClientSecret { get; set; }
|
|
||||||
|
|
||||||
public string? Audience { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api.Models;
|
|
||||||
|
|
||||||
public sealed class TokenErrorResponse
|
|
||||||
{
|
|
||||||
[JsonPropertyName("error")]
|
|
||||||
public string? Error { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("error_description")]
|
|
||||||
public string? ErrorDescription { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("error_uri")]
|
|
||||||
public string? ErrorUri { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api.Models;
|
|
||||||
|
|
||||||
public sealed class TokenRequest
|
|
||||||
{
|
|
||||||
[JsonPropertyName("grant_type")]
|
|
||||||
[AliasAs("grant_type")]
|
|
||||||
public string? GrantType { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("client_id")]
|
|
||||||
[AliasAs("client_id")]
|
|
||||||
public string? ClientId { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("client_secret")]
|
|
||||||
[AliasAs("client_secret")]
|
|
||||||
public string? ClientSecret { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("scope")]
|
|
||||||
[AliasAs("scope")]
|
|
||||||
public string? Scope { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api.Models;
|
|
||||||
|
|
||||||
public sealed class TokenResponse
|
|
||||||
{
|
|
||||||
[JsonPropertyName("access_token")]
|
|
||||||
public string? AccessToken { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("expires_in")]
|
|
||||||
public int ExpiresIn { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("token_type")]
|
|
||||||
public string? TokenType { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("scope")]
|
|
||||||
public string? Scope { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Api;
|
|
||||||
|
|
||||||
public sealed class OpenApiBearerSecuritySchemeTransformer : IOpenApiDocumentTransformer
|
|
||||||
{
|
|
||||||
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var bearerScheme = new OpenApiSecurityScheme
|
|
||||||
{
|
|
||||||
Type = SecuritySchemeType.Http,
|
|
||||||
Scheme = "bearer",
|
|
||||||
Description = "JWT Authorization header using the Bearer scheme",
|
|
||||||
};
|
|
||||||
|
|
||||||
document.AddComponent("Bearer", bearerScheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace PostFundManagement.Domain;
|
||||||
|
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Email;
|
|
||||||
|
|
||||||
public sealed class Account
|
|
||||||
{
|
|
||||||
public string? Username { get; set; }
|
|
||||||
|
|
||||||
public string? Password { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Email;
|
|
||||||
|
|
||||||
public sealed class SmtpSettings
|
|
||||||
{
|
|
||||||
public Account? Credentials { get; set; }
|
|
||||||
|
|
||||||
public int Port { get; set; }
|
|
||||||
|
|
||||||
public string? Host { get; set; }
|
|
||||||
|
|
||||||
public bool UseSsl { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Activity : IEntityTypeConfiguration<Domain.Entities.Activity>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Activity> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Activity).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.MilestoneId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.ApprovedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.PerformedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Category).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
|
||||||
builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Milestone)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.MilestoneId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Approver)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ApprovedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class AuditLog : IEntityTypeConfiguration<Domain.Entities.AuditLog>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.AuditLog> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.AuditLog).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.EntityName).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.EntityId).IsRequired();
|
|
||||||
builder.Property(f => f.Action).IsRequired().HasMaxLength(ShortLabelLength);
|
|
||||||
builder.Property(f => f.Changes).IsRequired(false).HasColumnType("jsonb");
|
|
||||||
builder.Property(f => f.PerformedBy).IsRequired();
|
|
||||||
builder.Property(f => f.Timestamp).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Performer)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.PerformedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Award : IEntityTypeConfiguration<Domain.Entities.Award>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Award> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Award).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.OrganisationId).IsRequired();
|
|
||||||
builder.Property(f => f.ProgrammeId).IsRequired();
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.ApprovedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Organisation)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.OrganisationId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Programme)
|
|
||||||
.WithMany(f => f.Awards)
|
|
||||||
.HasForeignKey(fk => fk.ProgrammeId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany(f => f.Awards)
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Approver)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ApprovedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Beneficiary : IEntityTypeConfiguration<Domain.Entities.Beneficiary>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Beneficiary> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Beneficiary).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UserId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Category).IsRequired().HasMaxLength(ShortLabelLength);
|
|
||||||
builder.Property(f => f.TargetCount).IsRequired();
|
|
||||||
builder.Property(f => f.ReachedCount).IsRequired();
|
|
||||||
builder.Property(f => f.Location).IsRequired();
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.User)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UserId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Budget : IEntityTypeConfiguration<Domain.Entities.Budget>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Budget> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Budget).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.ApprovedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Approver)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ApprovedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class ChangeRequest : IEntityTypeConfiguration<Domain.Entities.ChangeRequest>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.ChangeRequest> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.ChangeRequest).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.ApprovedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Justification).IsRequired().HasMaxLength(TextLength);
|
|
||||||
builder.Property(f => f.RevisedBudget).IsRequired(false).HasPrecision(18, 2);
|
|
||||||
builder.Property(f => f.RevisedEndedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Approver)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ApprovedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class ComplianceItem : IEntityTypeConfiguration<Domain.Entities.ComplianceItem>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.ComplianceItem> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.ComplianceItem).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.VerifiedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Requirements).IsRequired().HasMaxLength(MediumTextLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Verifier)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.VerifiedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Contract : IEntityTypeConfiguration<Domain.Entities.Contract>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Contract> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Contract).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.HasIndex(f => f.AwardId).IsUnique();
|
|
||||||
|
|
||||||
builder.Property(f => f.AwardId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.ExternalSignatureId).IsRequired(false);
|
|
||||||
builder.Property(f => f.SignedDocumentUrl).IsRequired(false);
|
|
||||||
builder.Property(f => f.EffectiveAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.ExpiresAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.TotalValue).IsRequired().HasPrecision(18, 2);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Award)
|
|
||||||
.WithOne(f => f.Contract)
|
|
||||||
.HasForeignKey<Domain.Entities.Contract>(fk => fk.AwardId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Disbursement : IEntityTypeConfiguration<Domain.Entities.Disbursement>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Disbursement> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Disbursement).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.MilestoneId).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Milestone)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.MilestoneId)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Evidence : IEntityTypeConfiguration<Domain.Entities.Evidence>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Evidence> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Evidence).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(f => f.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.MilestoneId).IsRequired();
|
|
||||||
builder.Property(f => f.IndicatorId).IsRequired();
|
|
||||||
builder.Property(f => f.ActivityId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Version).IsRequired().HasDefaultValue(1);
|
|
||||||
builder.Property(f => f.DocumentUrl).IsRequired();
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Type).IsRequired().HasConversion<int>().HasDefaultValueSql("9");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Milestone)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.MilestoneId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Indicator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.IndicatorId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Activity)
|
|
||||||
.WithMany(f => f.Evidences)
|
|
||||||
.HasForeignKey(fk => fk.ActivityId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Indicator : IEntityTypeConfiguration<Domain.Entities.Indicator>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Indicator> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Indicator).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.UnitOfMeasure).IsRequired().HasConversion<int>().HasDefaultValueSql("2");
|
|
||||||
builder.Property(f => f.BaselineAmount).IsRequired().HasPrecision(18, 2);
|
|
||||||
builder.Property(f => f.TargetAmount).IsRequired().HasPrecision(18, 2);
|
|
||||||
builder.Property(f => f.ActualAmount).IsRequired().HasPrecision(18, 2);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Instrument : IEntityTypeConfiguration<Domain.Entities.Instrument>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Instrument> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Instrument).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProgrammeId).IsRequired();
|
|
||||||
builder.Property(f => f.Code).IsRequired().HasMaxLength(ShortLabelLength);
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("2");
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Programme)
|
|
||||||
.WithMany(f => f.Instruments)
|
|
||||||
.HasForeignKey(fk => fk.ProgrammeId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Invite : IEntityTypeConfiguration<Domain.Entities.Invite>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Invite> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Invite).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.OrganisationId).IsRequired();
|
|
||||||
builder.Property(f => f.InvitedOrganisationId).IsRequired();
|
|
||||||
builder.Property(f => f.AwardId).IsRequired();
|
|
||||||
builder.Property(f => f.ContractId).IsRequired();
|
|
||||||
builder.Property(f => f.Recipient).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Organisation)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.OrganisationId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.InvitedOrganisation)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.InvitedOrganisationId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Award)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.AwardId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Contract)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ContractId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.RecipientUser)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.Recipient)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Issue : IEntityTypeConfiguration<Domain.Entities.Issue>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Issue> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Issue).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.ResolvedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.AssignedTo).IsRequired(false);
|
|
||||||
builder.Property(f => f.Title).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength);
|
|
||||||
builder.Property(f => f.Priority).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Resolution).IsRequired();
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Assignee)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.AssignedTo)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.SetNull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Milestone : IEntityTypeConfiguration<Domain.Entities.Milestone>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Milestone> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Milestone).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.DueAt).IsRequired();
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Priority).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Notification : IEntityTypeConfiguration<Domain.Entities.Notification>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Notification> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Notification).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.SentAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Recipient).IsRequired();
|
|
||||||
builder.Property(f => f.OrganisationId).IsRequired();
|
|
||||||
builder.Property(f => f.Platform).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Subject).IsRequired(false).HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Message).IsRequired().HasMaxLength(LargeTextLength);
|
|
||||||
builder.Property(f => f.Destination).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Error).IsRequired(false);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Organisation)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.OrganisationId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.RecipientUser)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.Recipient)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Organisation : IEntityTypeConfiguration<Domain.Entities.Organisation>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Organisation> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Organisation).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.RegistrationNo).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Email).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Type).IsRequired().HasConversion<int>().HasDefaultValueSql("5");
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Outcome : IEntityTypeConfiguration<Domain.Entities.Outcome>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Outcome> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Outcome).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.BeneficiaryId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Beneficiary)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.BeneficiaryId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Portfolio : IEntityTypeConfiguration<Domain.Entities.Portfolio>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Portfolio> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Portfolio).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.OwnedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired(false).HasMaxLength(TextLength);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Owner)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.OwnedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Programme : IEntityTypeConfiguration<Domain.Entities.Programme>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Programme> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Programme).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.PortfolioId).IsRequired();
|
|
||||||
builder.Property(f => f.OwnedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Portfolio)
|
|
||||||
.WithMany(f => f.Programmes)
|
|
||||||
.HasForeignKey(fk => fk.PortfolioId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Owner)
|
|
||||||
.WithMany()
|
|
||||||
.IsRequired()
|
|
||||||
.HasForeignKey(fk => fk.OwnedBy)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Project : IEntityTypeConfiguration<Domain.Entities.Project>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Project> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Project).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProgrammeId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.StartedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.EndedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength);
|
|
||||||
builder.Property(f => f.Sector).IsRequired().HasConversion<int>().HasDefaultValueSql("11");
|
|
||||||
builder.Property(f => f.ProjectType).IsRequired().HasConversion<int>().HasDefaultValueSql("10");
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Programme)
|
|
||||||
.WithMany(f => f.Projects)
|
|
||||||
.HasForeignKey(fk => fk.ProgrammeId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Risk : IEntityTypeConfiguration<Domain.Entities.Risk>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Risk> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Risk).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.Likelihood).IsRequired().HasConversion<int>().HasDefaultValueSql("3");
|
|
||||||
builder.Property(f => f.Impact).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(TextLength);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class Rule : IEntityTypeConfiguration<Domain.Entities.Rule>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.Rule> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.Rule).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(fk => fk.Id);
|
|
||||||
builder.Property(f => f.ProgrammeId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.UpdatedBy).IsRequired(false);
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
|
||||||
builder.Property(f => f.Name).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Version).IsRequired().HasDefaultValue(1);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("2");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Programme)
|
|
||||||
.WithMany(f => f.Rules)
|
|
||||||
.HasForeignKey(fk => fk.ProgrammeId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Updator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.UpdatedBy)
|
|
||||||
.IsRequired(false)
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class SiteVisit : IEntityTypeConfiguration<Domain.Entities.SiteVisit>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.SiteVisit> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.SiteVisit).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.ProjectId).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedBy).IsRequired();
|
|
||||||
builder.Property(f => f.CreatedAt).IsRequired().HasDefaultValueSql("now()");
|
|
||||||
builder.Property(f => f.VisitedAt).IsRequired();
|
|
||||||
builder.Property(f => f.InspectorName).IsRequired();
|
|
||||||
builder.Property(f => f.Findings).IsRequired(false).HasMaxLength(LargeTextLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("1");
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Project)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.ProjectId)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
|
||||||
|
|
||||||
builder.HasOne(f => f.Creator)
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey(fk => fk.CreatedBy)
|
|
||||||
.IsRequired()
|
|
||||||
.OnDelete(DeleteBehavior.NoAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Configuration.Entities;
|
|
||||||
|
|
||||||
public sealed class User : IEntityTypeConfiguration<Domain.Entities.User>
|
|
||||||
{
|
|
||||||
public void Configure(EntityTypeBuilder<Domain.Entities.User> builder)
|
|
||||||
{
|
|
||||||
builder.ToTable(nameof(Domain.Entities.User).Pluralize());
|
|
||||||
|
|
||||||
builder.HasKey(pk => pk.Id);
|
|
||||||
builder.Property(f => f.Email).IsRequired().HasMaxLength(LabelLength);
|
|
||||||
builder.Property(f => f.Status).IsRequired().HasConversion<int>().HasDefaultValueSql("2");
|
|
||||||
builder.Property(f => f.LastLoginAt).IsRequired(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.Hash;
|
|
||||||
|
|
||||||
public sealed class HasherSettings
|
|
||||||
{
|
|
||||||
public string? Salt { get; set; }
|
|
||||||
|
|
||||||
public int MinHashLength { get; set; }
|
|
||||||
|
|
||||||
public string? PayfastPassphrase { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Configuration.S3;
|
|
||||||
|
|
||||||
public sealed class S3Settings
|
|
||||||
{
|
|
||||||
public string? ServiceUrl { get; set; }
|
|
||||||
|
|
||||||
public string? AccessKey { get; set; }
|
|
||||||
|
|
||||||
public string? SecretKey { get; set; }
|
|
||||||
|
|
||||||
public string? BucketName { get; set; }
|
|
||||||
|
|
||||||
public string? Region { get; set; }
|
|
||||||
|
|
||||||
public string? CdnBaseUrl { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Activity, Activity>]
|
|
||||||
public class Activity : Models.Activity
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Approver { get; set; }
|
|
||||||
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual Milestone? Milestone { get; set; }
|
|
||||||
|
|
||||||
public virtual ICollection<Evidence> Evidences { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.AuditLog, AuditLog>]
|
|
||||||
public class AuditLog : Models.AuditLog
|
|
||||||
{
|
|
||||||
public virtual User? Performer { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Award, Award>]
|
|
||||||
public class Award : Models.Award
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Approver { get; set; }
|
|
||||||
|
|
||||||
public virtual Organisation? Organisation { get; set; }
|
|
||||||
|
|
||||||
public virtual Programme? Programme { get; set; }
|
|
||||||
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual Contract? Contract { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Beneficiary, Beneficiary>]
|
|
||||||
public class Beneficiary : Models.Beneficiary
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? User { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Budget, Budget>]
|
|
||||||
public class Budget : Models.Budget
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Approver { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.ChangeRequest, ChangeRequest>]
|
|
||||||
public class ChangeRequest : Models.ChangeRequest
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Approver { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.ComplianceItem, ComplianceItem>]
|
|
||||||
public class ComplianceItem : Models.ComplianceItem
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Verifier { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Contract, Contract>]
|
|
||||||
public class Contract : Models.Contract
|
|
||||||
{
|
|
||||||
public virtual Award? Award { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Disbursement, Disbursement>]
|
|
||||||
public class Disbursement : Models.Disbursement
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual Milestone? Milestone { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Evidence, Evidence>]
|
|
||||||
public class Evidence : Models.Evidence
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual Milestone? Milestone { get; set; }
|
|
||||||
|
|
||||||
public virtual Indicator? Indicator { get; set; }
|
|
||||||
|
|
||||||
public virtual Activity? Activity { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Indicator, Indicator>]
|
|
||||||
public class Indicator : Models.Indicator
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Instrument, Instrument>]
|
|
||||||
public class Instrument : Models.Instrument
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual Programme? Programme { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Invite, Invite>]
|
|
||||||
public class Invite : Models.Invite
|
|
||||||
{
|
|
||||||
public virtual Organisation? Organisation { get; set; }
|
|
||||||
|
|
||||||
public virtual Organisation? InvitedOrganisation { get; set; }
|
|
||||||
|
|
||||||
public virtual Award? Award { get; set; }
|
|
||||||
|
|
||||||
public virtual Contract? Contract { get; set; }
|
|
||||||
|
|
||||||
public virtual User? RecipientUser { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Issue, Issue>]
|
|
||||||
public class Issue : Models.Issue
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Assignee { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Milestone, Milestone>]
|
|
||||||
public class Milestone : Models.Milestone
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Notification, Notification>]
|
|
||||||
public class Notification : Models.Notification
|
|
||||||
{
|
|
||||||
public virtual Organisation? Organisation { get; set; }
|
|
||||||
|
|
||||||
public virtual User? RecipientUser { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Organisation, Organisation>]
|
|
||||||
public class Organisation : Models.Organisation
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Outcome, Outcome>]
|
|
||||||
public class Outcome : Models.Outcome
|
|
||||||
{
|
|
||||||
public virtual Beneficiary? Beneficiary { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Portfolio, Portfolio>]
|
|
||||||
public class Portfolio : Models.Portfolio
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Owner { get; set; }
|
|
||||||
|
|
||||||
public virtual ICollection<Programme> Programmes { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Programme, Programme>]
|
|
||||||
public class Programme : Models.Programme
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Owner { get; set; }
|
|
||||||
|
|
||||||
public virtual Portfolio? Portfolio { get; set; }
|
|
||||||
|
|
||||||
public virtual ICollection<Instrument> Instruments { get; set; } = [];
|
|
||||||
|
|
||||||
public virtual ICollection<Rule> Rules { get; set; } = [];
|
|
||||||
|
|
||||||
public virtual ICollection<Project> Projects { get; set; } = [];
|
|
||||||
|
|
||||||
public virtual ICollection<Award> Awards { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Project, Project>]
|
|
||||||
public class Project : Models.Project
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
|
|
||||||
public virtual Programme? Programme { get; set; }
|
|
||||||
|
|
||||||
public virtual ICollection<Award> Awards { get; set; } = [];
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Risk, Risk>]
|
|
||||||
public class Risk : Models.Risk
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.Rule, Rule>]
|
|
||||||
public class Rule : Models.Rule
|
|
||||||
{
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Updator { get; set; }
|
|
||||||
|
|
||||||
public virtual Programme? Programme { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.SiteVisit, SiteVisit>]
|
|
||||||
public class SiteVisit : Models.SiteVisit
|
|
||||||
{
|
|
||||||
public virtual Project? Project { get; set; }
|
|
||||||
|
|
||||||
public virtual User? Creator { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Entities;
|
|
||||||
|
|
||||||
[EntityTypeConfiguration<Configuration.Entities.User, User>]
|
|
||||||
public sealed class User : Models.User;
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain;
|
|
||||||
|
|
||||||
public enum EmailStatuses : int
|
|
||||||
{
|
|
||||||
GeneralError = 0,
|
|
||||||
AuthenticationError = 1,
|
|
||||||
ProtocolError = 2,
|
|
||||||
Connected = 3,
|
|
||||||
Disconnected = 4,
|
|
||||||
TooManyConnections = 5,
|
|
||||||
ConnectionAborted = 6,
|
|
||||||
Success = 7
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum NotificationStatus : int
|
|
||||||
{
|
|
||||||
Pending = 1,
|
|
||||||
Queued = 2,
|
|
||||||
Processing = 3,
|
|
||||||
Sent = 4,
|
|
||||||
Delivered = 5,
|
|
||||||
Read = 6,
|
|
||||||
Failed = 7,
|
|
||||||
Cancelled = 8,
|
|
||||||
Bounced = 9
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum NotificationPlatform : int
|
|
||||||
{
|
|
||||||
Email = 1,
|
|
||||||
SMS = 2,
|
|
||||||
WhatsApp = 3,
|
|
||||||
PushNotification = 4,
|
|
||||||
InApp = 5,
|
|
||||||
Teams = 6,
|
|
||||||
Slack = 7,
|
|
||||||
Webhook = 8,
|
|
||||||
Other = 99
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum EvidenceType : int
|
|
||||||
{
|
|
||||||
AttendanceRegister = 1,
|
|
||||||
BankStatement = 2,
|
|
||||||
CompletionCertificate = 3,
|
|
||||||
FinancialInvoiceOrReceipt = 4,
|
|
||||||
GeotaggedPhoto = 5,
|
|
||||||
InspectionReport = 6,
|
|
||||||
MeetingMinutes = 7,
|
|
||||||
PayrollOrStipendRegister = 8,
|
|
||||||
ProcurementDocument = 9,
|
|
||||||
TechnicalSpecificationOrDesign = 10,
|
|
||||||
VideoRecording = 11,
|
|
||||||
Other = 99
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ProjectType : int
|
|
||||||
{
|
|
||||||
CapacityBuildingAndTraining = 1,
|
|
||||||
CapitalExpenditureAndInfrastructure = 2,
|
|
||||||
CommercializationAndScaling = 3,
|
|
||||||
FeasibilityAndTechnicalAssistance = 4,
|
|
||||||
OperationalGrantAndSubsidy = 5,
|
|
||||||
ProductDevelopmentAndPrototyping = 6,
|
|
||||||
ResearchAndDevelopment = 7,
|
|
||||||
SocialImpactAndCommunityDevelopment = 8,
|
|
||||||
TechnologyTransferAndAdoption = 9,
|
|
||||||
Innovation = 10,
|
|
||||||
Other = 99
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum IndustrySector : int
|
|
||||||
{
|
|
||||||
AgricultureAndAgroProcessing = 1,
|
|
||||||
AutomotiveAndTransportEquipment = 2,
|
|
||||||
ChemicalsAndPharmaceuticals = 3,
|
|
||||||
ConstructionAndInfrastructure = 4,
|
|
||||||
CreativeIndustriesAndMedia = 5,
|
|
||||||
DefenseAndAerospace = 6,
|
|
||||||
EducationAndSkillsDevelopment = 7,
|
|
||||||
EnergyAndUtilities = 8,
|
|
||||||
FinancialServices = 9,
|
|
||||||
HealthcareAndLifeSciences = 10,
|
|
||||||
InformationCommunicationTechnology = 11,
|
|
||||||
ManufacturingAndIndustrial = 12,
|
|
||||||
MiningAndMetals = 13,
|
|
||||||
RealEstateAndHousing = 14,
|
|
||||||
RetailAndConsumerGoods = 15,
|
|
||||||
TourismAndHospitality = 16,
|
|
||||||
WaterAndSanitation = 17,
|
|
||||||
Other = 99
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum OrganisationType : int
|
|
||||||
{
|
|
||||||
// Non-Profit & Civil Society
|
|
||||||
NonGovernmentalOrganisation = 1, // NGO / NPO
|
|
||||||
CivilSocietyOrganisation = 2, // CSO / Community-based group
|
|
||||||
Trust = 3, // Charitable Trust / Foundation
|
|
||||||
FaithBasedOrganisation = 4, // FBO
|
|
||||||
|
|
||||||
// Commercial & Private Sector
|
|
||||||
PrivateLimitedCompany = 5, // Pty Ltd / Ltd
|
|
||||||
PublicLimitedCompany = 6, // PLC
|
|
||||||
SoleProprietorship = 7, // Individual / Sole Trader
|
|
||||||
Partnership = 8, // General / Limited Partnership
|
|
||||||
SocialEnterprise = 9, // Revenue-generating impact entity
|
|
||||||
|
|
||||||
// Public Sector & Government
|
|
||||||
NationalGovernmentMinistry = 10, // Department / Ministry
|
|
||||||
ProvincialGovernmentEntity = 11, // State / Provincial authority
|
|
||||||
LocalGovernmentMunicipality = 12,// District / City Council
|
|
||||||
StateOwnedEnterprise = 13, // Parastatal / SOE
|
|
||||||
PublicAgency = 14, // Statutory body / Regulator
|
|
||||||
|
|
||||||
// Academia & Research
|
|
||||||
PublicUniversity = 15,
|
|
||||||
PrivateUniversity = 16,
|
|
||||||
ResearchInstitute = 17, // Science / Policy think tank
|
|
||||||
TechnicalVocationalCollege = 18, // TVET / Vocational school
|
|
||||||
|
|
||||||
// Cooperative & Financial
|
|
||||||
Cooperative = 19, // Producer / Worker co-op
|
|
||||||
MicrofinanceInstitution = 20, // MFI / Financial Intermediary
|
|
||||||
CommercialBank = 21,
|
|
||||||
DevelopmentFinanceInstitution = 22, // DFI / Multilateral Bank
|
|
||||||
|
|
||||||
// International & Multilateral
|
|
||||||
UnitedNationsAgency = 23, // UN / UNDP / UNICEF
|
|
||||||
IntergovernmentalOrganisation = 24, // IGO (e.g., AU, SADC, EU)
|
|
||||||
InternationalNGO = 25, // INGO
|
|
||||||
|
|
||||||
// Consortium & Special Vehicles
|
|
||||||
Consortium = 26, // Unincorporated joint venture
|
|
||||||
SpecialPurposeVehicle = 27 // SPV / Joint Venture Entity
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Priority : int
|
|
||||||
{
|
|
||||||
Low = 1,
|
|
||||||
Medium = 2,
|
|
||||||
High = 3,
|
|
||||||
Critical = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum UnitOfMeasure : int
|
|
||||||
{
|
|
||||||
Count = 1,
|
|
||||||
Percentage = 2,
|
|
||||||
Currency = 3,
|
|
||||||
Area = 4,
|
|
||||||
Weight = 5,
|
|
||||||
Volume = 6,
|
|
||||||
DurationDays = 7,
|
|
||||||
DurationMonths = 8,
|
|
||||||
Ratio = 9
|
|
||||||
}
|
|
||||||
public enum ContractStatus : int
|
|
||||||
{
|
|
||||||
Draft = 1,
|
|
||||||
PendingApproval = 2,
|
|
||||||
Active = 3,
|
|
||||||
Expired = 4,
|
|
||||||
Terminated = 5,
|
|
||||||
Amended = 6
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RiskLikelihood : int
|
|
||||||
{
|
|
||||||
Rare = 1,
|
|
||||||
Unlikely = 2,
|
|
||||||
Possible = 3,
|
|
||||||
Likely = 4,
|
|
||||||
AlmostCertain = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RiskImpact : int
|
|
||||||
{
|
|
||||||
Negligible = 1,
|
|
||||||
Minor = 2,
|
|
||||||
Moderate = 3,
|
|
||||||
Major = 4,
|
|
||||||
Critical = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ApprovalStatus : int
|
|
||||||
{
|
|
||||||
Pending = 1,
|
|
||||||
Review = 2,
|
|
||||||
Approved = 3,
|
|
||||||
Rejected = 4,
|
|
||||||
Removed = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ActivityStatus : int
|
|
||||||
{
|
|
||||||
Active = 1,
|
|
||||||
Inactive = 2,
|
|
||||||
Disabled = 3
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
|
||||||
using PostFundManagement.Domain.Api;
|
|
||||||
using PostFundManagement.Domain.Api.Configuration;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Api
|
|
||||||
{
|
|
||||||
public static void ConfigureCookieOidcSameSiteSupport(this IServiceCollection services) =>
|
|
||||||
services.Configure<CookiePolicyOptions>(options =>
|
|
||||||
{
|
|
||||||
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
|
|
||||||
options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
|
|
||||||
options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
|
|
||||||
});
|
|
||||||
|
|
||||||
public static void CheckSameSite(HttpContext httpContext, CookieOptions options)
|
|
||||||
{
|
|
||||||
if (options.SameSite == SameSiteMode.None)
|
|
||||||
{
|
|
||||||
bool isSecure = httpContext.Request.IsHttps;
|
|
||||||
|
|
||||||
if (!isSecure && httpContext.Request.Headers.TryGetValue("X-Forwarded-Proto", out var proto))
|
|
||||||
isSecure = string.Equals(proto, "https", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (!isSecure && httpContext.Request.Headers.TryGetValue("Forwarded", out var forwarded))
|
|
||||||
isSecure = forwarded.ToString().Contains("proto=https", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (!isSecure) options.SameSite = SameSiteMode.Unspecified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddLiteCharmsApiSecurity(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var configSection = configuration.GetSection(nameof(SecuritySettings));
|
|
||||||
|
|
||||||
var authOptions = new SecuritySettings();
|
|
||||||
configSection.Bind(authOptions);
|
|
||||||
|
|
||||||
services.Configure<SecuritySettings>(configSection);
|
|
||||||
|
|
||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
||||||
.AddJwtBearer(options =>
|
|
||||||
{
|
|
||||||
options.Authority = authOptions.Authority;
|
|
||||||
options.Audience = authOptions.Audience;
|
|
||||||
options.TokenValidationParameters = new TokenValidationParameters
|
|
||||||
{
|
|
||||||
ValidIssuer = authOptions.Authority,
|
|
||||||
ValidateAudience = true,
|
|
||||||
ValidateIssuer = true,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddAuthorization();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static WebApplication AddSecurityEndpoints(this WebApplication app)
|
|
||||||
{
|
|
||||||
app.MapGet("/login", async (HttpContext context, string redirectUri = "/") =>
|
|
||||||
{
|
|
||||||
await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties
|
|
||||||
{
|
|
||||||
RedirectUri = redirectUri,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.MapGet("/logout", async (HttpContext context, string? redirectUri = null) =>
|
|
||||||
{
|
|
||||||
var idToken = await context.GetTokenAsync("id_token");
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(redirectUri))
|
|
||||||
{
|
|
||||||
var host = context.Request.Host.ToUriComponent();
|
|
||||||
redirectUri = $"https://{host}/";
|
|
||||||
}
|
|
||||||
|
|
||||||
var authProperties = new AuthenticationProperties { RedirectUri = redirectUri, };
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(idToken))
|
|
||||||
authProperties.Parameters.Add("id_token_hint", idToken);
|
|
||||||
|
|
||||||
await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, authProperties);
|
|
||||||
});
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddApiServices(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddHttpClient();
|
|
||||||
|
|
||||||
services.AddApiVersioning(options =>
|
|
||||||
{
|
|
||||||
options.ReportApiVersions = true;
|
|
||||||
options.AssumeDefaultVersionWhenUnspecified = true;
|
|
||||||
options.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
|
|
||||||
new QueryStringApiVersionReader("version"),
|
|
||||||
new QueryStringApiVersionReader("version"),
|
|
||||||
new MediaTypeApiVersionReader("version"));
|
|
||||||
})
|
|
||||||
.AddApiExplorer(options =>
|
|
||||||
{
|
|
||||||
options.GroupNameFormat = "'v'VVV";
|
|
||||||
options.SubstituteApiVersionInUrl = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
var urls = configuration["ASPNETCORE_URLS"] ?? configuration["Urls"];
|
|
||||||
var healthUrl = "http://localhost:8080/health";
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(urls))
|
|
||||||
{
|
|
||||||
string firstUrl = urls.Split(';').FirstOrDefault(s => s.Contains("http://", StringComparison.InvariantCultureIgnoreCase))!
|
|
||||||
.Replace("0.0.0.0", "localhost")
|
|
||||||
.Replace("*", "localhost")
|
|
||||||
.Replace("+", "localhost");
|
|
||||||
|
|
||||||
healthUrl = $"{firstUrl.TrimEnd('/')}/health";
|
|
||||||
}
|
|
||||||
|
|
||||||
services.AddHealthChecksUI(setup =>
|
|
||||||
{
|
|
||||||
setup.SetNotifyUnHealthyOneTimeUntilChange();
|
|
||||||
setup.AddHealthCheckEndpoint("primary, heal", healthUrl);
|
|
||||||
setup.SetHeaderText("Midrand Books");
|
|
||||||
})
|
|
||||||
.AddInMemoryStorage();
|
|
||||||
|
|
||||||
services.AddOutputCache(options =>
|
|
||||||
{
|
|
||||||
options.AddBasePolicy(builder => builder.Cache());
|
|
||||||
options.DefaultExpirationTimeSpan = TimeSpan.FromSeconds(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddOpenApi(options => options.AddDocumentTransformer<OpenApiBearerSecuritySchemeTransformer>());
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IApplicationBuilder MapEndpoints(this WebApplication app, IDictionary<int, RouteGroupBuilder> versionGroups)
|
|
||||||
{
|
|
||||||
var endpoints = app.Services.GetRequiredService<IEnumerable<IEndpoint>>();
|
|
||||||
|
|
||||||
foreach (var endpoint in endpoints)
|
|
||||||
{
|
|
||||||
var versionAttributes = endpoint.GetType().GetCustomAttributes<ApiVersionTargetAttribute>().ToList();
|
|
||||||
|
|
||||||
if (versionAttributes.Count != 0)
|
|
||||||
{
|
|
||||||
foreach (var attr in versionAttributes)
|
|
||||||
if (versionGroups.TryGetValue(attr.MajorVersion, out var targetGroup))
|
|
||||||
endpoint.Map(targetGroup);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
endpoint.Map(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddEndpoints(this IServiceCollection services, Assembly assembly)
|
|
||||||
{
|
|
||||||
ServiceDescriptor[] discriptors = [.. assembly.DefinedTypes
|
|
||||||
.Where(t => t is { IsInterface: false, IsAbstract: false })
|
|
||||||
.Where(t => t.IsAssignableTo(typeof(IEndpoint)))
|
|
||||||
.Select(t => ServiceDescriptor.Transient(typeof(IEndpoint), t))];
|
|
||||||
|
|
||||||
services.TryAddEnumerable(discriptors);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToEndpointName(this Type target, string? annotation = "") =>
|
|
||||||
$"{target.Name.Replace("Endpoint", string.Empty)}{annotation}".ToLower(CultureInfo.CurrentCulture);
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Constants
|
|
||||||
{
|
|
||||||
public const string DefaultSchedulerName = "pfm-scheduler";
|
|
||||||
|
|
||||||
public const string GeneralS3SettingsSection = "PfmS3Settings";
|
|
||||||
|
|
||||||
public const string EvidenceS3SettingsSection = "EvidenceS3Settings";
|
|
||||||
|
|
||||||
public const string ContractS3SettingsSection = "ContractS3Settings";
|
|
||||||
|
|
||||||
public const string GeneralBucketName = "pfm.general";
|
|
||||||
|
|
||||||
public const string EvidenceBucketName = "pfm.evidence";
|
|
||||||
|
|
||||||
public const string ContractBucketName = "pfm.contract";
|
|
||||||
|
|
||||||
public const string DatabaseConfigName = "PfmDatabase";
|
|
||||||
|
|
||||||
public const int LabelLength = 256;
|
|
||||||
|
|
||||||
public const int ShortLabelLength = 100;
|
|
||||||
|
|
||||||
public const int TextLength = 1024;
|
|
||||||
|
|
||||||
public const int MediumTextLength = 2048;
|
|
||||||
|
|
||||||
public const int LargeTextLength = 4096;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Configuration.Email;
|
|
||||||
using PostFundManagement.Domain.Services;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Email
|
|
||||||
{
|
|
||||||
public const string EmailFromName = "PFM Team";
|
|
||||||
public const string EmailFromAddress = "info@pfm.co.za"; // TODO: replace with real address
|
|
||||||
|
|
||||||
public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.Configure<SmtpSettings>(configuration.GetSection("Email"));
|
|
||||||
|
|
||||||
services.AddSingleton<EmailService>();
|
|
||||||
|
|
||||||
services.AddOpenTelemetry()
|
|
||||||
.WithTracing(tracing => tracing.AddSource("Pfm.EmailService"))
|
|
||||||
.WithMetrics(metrics => metrics.AddMeter("Pfm.EmailService"));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.Metrics;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class EmailTelemetry
|
|
||||||
{
|
|
||||||
public static readonly ActivitySource Source = new("LiteCharms.EmailService");
|
|
||||||
public static readonly Meter Meter = new("LiteCharms.EmailService");
|
|
||||||
public static readonly Counter<long> EmailsSent = Meter.CreateCounter<long>("emails_sent_total", "count", "Total successful emails sent");
|
|
||||||
public static readonly Counter<long> EmailsFailed = Meter.CreateCounter<long>("emails_failed_total", "count", "Total failed email attempts");
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Configuration.Hash;
|
|
||||||
using PostFundManagement.Domain.Services;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Hash
|
|
||||||
{
|
|
||||||
public const string HasherConfigSectionName = "HasherSettings";
|
|
||||||
|
|
||||||
public static IServiceCollection AddHashServices(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.Configure<HasherSettings>(configuration.GetSection(HasherConfigSectionName));
|
|
||||||
|
|
||||||
var settings = configuration.GetSection(HasherConfigSectionName).Get<HasherSettings>();
|
|
||||||
|
|
||||||
services.AddSingleton<IHashids>(_ =>
|
|
||||||
new Hashids(settings!.Salt, minHashLength: settings.MinHashLength));
|
|
||||||
|
|
||||||
services.AddSingleton<HashService>();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Models;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Mappers
|
|
||||||
{
|
|
||||||
public static Activity Map(this Entities.Activity entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Amount = entity.Amount,
|
|
||||||
ApprovedBy = entity.ApprovedBy,
|
|
||||||
Category = entity.Category,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
MilestoneId = entity.MilestoneId,
|
|
||||||
PerformedAt = entity.PerformedAt,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
Status = entity.Status,
|
|
||||||
Title = entity.Title,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static AuditLog Map(this Entities.AuditLog entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Action = entity.Action,
|
|
||||||
Changes = entity.Changes,
|
|
||||||
EntityId = entity.EntityId,
|
|
||||||
EntityName = entity.EntityName,
|
|
||||||
PerformedBy = entity.PerformedBy,
|
|
||||||
Timestamp = entity.Timestamp
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Award Map(this Entities.Award entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Amount = entity.Amount,
|
|
||||||
ApprovedBy = entity.ApprovedBy,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
OrganisationId = entity.OrganisationId,
|
|
||||||
ProgrammeId = entity.ProgrammeId,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Beneficiary Map(this Entities.Beneficiary entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Category = entity.Category,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Location = entity.Location,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
ReachedCount = entity.ReachedCount,
|
|
||||||
TargetCount = entity.TargetCount,
|
|
||||||
UserId = entity.UserId
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Budget Map(this Entities.Budget entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Amount = entity.Amount,
|
|
||||||
ApprovedBy = entity.ApprovedBy,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
UpdatedAt = entity.UpdatedAt
|
|
||||||
};
|
|
||||||
|
|
||||||
public static ComplianceItem Map(this Entities.ComplianceItem entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
Requirements = entity.Requirements,
|
|
||||||
Status = entity.Status,
|
|
||||||
Title = entity.Title,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
VerifiedBy = entity.VerifiedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Contract Map(this Entities.Contract entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
AwardId = entity.AwardId,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
EffectiveAt = entity.EffectiveAt,
|
|
||||||
ExpiresAt = entity.ExpiresAt,
|
|
||||||
ExternalSignatureId = entity.ExternalSignatureId,
|
|
||||||
SignedDocumentUrl = entity.SignedDocumentUrl,
|
|
||||||
Status = entity.Status,
|
|
||||||
TotalValue = entity.TotalValue
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Disbursement Map(this Entities.Disbursement entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Amount = entity.Amount,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
MilestoneId = entity.MilestoneId,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Evidence Map(this Entities.Evidence entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
DocumentUrl = entity.DocumentUrl,
|
|
||||||
IndicatorId = entity.IndicatorId,
|
|
||||||
MilestoneId = entity.MilestoneId,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy,
|
|
||||||
Version = entity.Version,
|
|
||||||
ActivityId = entity.ActivityId,
|
|
||||||
Type = entity.Type
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Indicator Map(this Entities.Indicator entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
ActualAmount = entity.ActualAmount,
|
|
||||||
BaselineAmount = entity.BaselineAmount,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
TargetAmount = entity.TargetAmount,
|
|
||||||
UnitOfMeasure = entity.UnitOfMeasure
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Instrument Map(this Entities.Instrument entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Code = entity.Code,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
Name = entity.Name,
|
|
||||||
Status = entity.Status,
|
|
||||||
ProgrammeId = entity.ProgrammeId
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Invite Map(this Entities.Invite entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
AwardId = entity.AwardId,
|
|
||||||
ContractId = entity.ContractId,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
InvitedOrganisationId = entity.InvitedOrganisationId,
|
|
||||||
OrganisationId = entity.OrganisationId,
|
|
||||||
Recipient = entity.Recipient,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Issue Map(this Entities.Issue entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
Priority = entity.Priority,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
ResolvedAt = entity.ResolvedAt,
|
|
||||||
Status = entity.Status,
|
|
||||||
Title = entity.Title,
|
|
||||||
AssignedTo = entity.AssignedTo,
|
|
||||||
Resolution = entity.Resolution
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Milestone Map(this Entities.Milestone entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
DueAt = entity.DueAt,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy,
|
|
||||||
Priority = entity.Priority
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Organisation Map(this Entities.Organisation entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Email = entity.Email,
|
|
||||||
Name = entity.Name,
|
|
||||||
RegistrationNo = entity.RegistrationNo,
|
|
||||||
Status = entity.Status,
|
|
||||||
Type = entity.Type
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Outcome Map(this Entities.Outcome entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
BeneficiaryId = entity.BeneficiaryId,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
Name = entity.Name
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Portfolio Map(this Entities.Portfolio entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
Name = entity.Name,
|
|
||||||
OwnedBy = entity.OwnedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Notification Map(this Entities.Notification entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
Destination = entity.Destination,
|
|
||||||
Error = entity.Error,
|
|
||||||
Message = entity.Message,
|
|
||||||
OrganisationId = entity.OrganisationId,
|
|
||||||
Platform = entity.Platform,
|
|
||||||
Recipient = entity.Recipient,
|
|
||||||
SentAt = entity.SentAt,
|
|
||||||
Status = entity.Status,
|
|
||||||
Subject = entity.Subject,
|
|
||||||
UpdatedAt = entity.UpdatedAt
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Programme Map(this Entities.Programme entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Name = entity.Name,
|
|
||||||
OwnedBy = entity.OwnedBy,
|
|
||||||
PortfolioId = entity.PortfolioId,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Project Map(this Entities.Project entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
EndedAt = entity.EndedAt,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProgrammeId = entity.ProgrammeId,
|
|
||||||
StartedAt = entity.StartedAt,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy,
|
|
||||||
ProjectType = entity.ProjectType,
|
|
||||||
Sector = entity.Sector
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Risk Map(this Entities.Risk entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Description = entity.Description,
|
|
||||||
Impact = entity.Impact,
|
|
||||||
Likelihood = entity.Likelihood,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProjectId = entity.ProjectId
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Rule Map(this Entities.Rule entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Name = entity.Name,
|
|
||||||
ProgrammeId = entity.ProgrammeId,
|
|
||||||
Status = entity.Status,
|
|
||||||
UpdatedAt = entity.UpdatedAt,
|
|
||||||
UpdatedBy = entity.UpdatedBy,
|
|
||||||
Version = entity.Version
|
|
||||||
};
|
|
||||||
|
|
||||||
public static SiteVisit Map(this Entities.SiteVisit entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
ProjectId = entity.ProjectId,
|
|
||||||
CreatedAt = entity.CreatedAt,
|
|
||||||
CreatedBy = entity.CreatedBy,
|
|
||||||
Findings = entity.Findings,
|
|
||||||
InspectorName = entity.InspectorName,
|
|
||||||
Status = entity.Status,
|
|
||||||
VisitedAt = entity.VisitedAt
|
|
||||||
};
|
|
||||||
|
|
||||||
public static User Map(this Entities.User entity) => new()
|
|
||||||
{
|
|
||||||
Id = entity.Id,
|
|
||||||
Email = entity.Email,
|
|
||||||
LastLoginAt = entity.LastLoginAt,
|
|
||||||
Status = entity.Status
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.Metrics;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class MediatorTelemetry
|
|
||||||
{
|
|
||||||
public const string ServiceName = "LiteCharms.Mediator";
|
|
||||||
|
|
||||||
public static readonly ActivitySource Source = new(ServiceName);
|
|
||||||
public static readonly Meter Meter = new(ServiceName);
|
|
||||||
|
|
||||||
public static readonly Counter<long> RequestCounter = Meter.CreateCounter<long>("mediator_requests_total");
|
|
||||||
public static readonly Histogram<double> RequestDuration = Meter.CreateHistogram<double>("mediator_request_duration_ms");
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Otel
|
|
||||||
{
|
|
||||||
public static WebApplicationBuilder AddWebTelemetry(this WebApplicationBuilder builder)
|
|
||||||
{
|
|
||||||
var serviceName = builder.Configuration.GetValue<string>("Monitoring:ServiceName") ?? "Pfm";
|
|
||||||
var endpoint = builder.Configuration.GetValue<string>("Monitoring:Address")!;
|
|
||||||
var apiKey = builder.Configuration.GetValue<string>("Monitoring:ApiKey");
|
|
||||||
|
|
||||||
var resourceBuilder = ResourceBuilder.CreateDefault()
|
|
||||||
.AddService(serviceName);
|
|
||||||
|
|
||||||
var otlpHeaders = !string.IsNullOrEmpty(apiKey) ? $"x-otlp-api-key={apiKey}" : null;
|
|
||||||
|
|
||||||
builder.Logging.AddOpenTelemetry(logging =>
|
|
||||||
{
|
|
||||||
logging.SetResourceBuilder(resourceBuilder);
|
|
||||||
logging.AddOtlpExporter(opt =>
|
|
||||||
{
|
|
||||||
opt.Endpoint = new Uri(endpoint);
|
|
||||||
opt.Protocol = OtlpExportProtocol.Grpc;
|
|
||||||
opt.Headers = otlpHeaders;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddOpenTelemetry()
|
|
||||||
.WithTracing(tracing => tracing
|
|
||||||
.SetResourceBuilder(resourceBuilder)
|
|
||||||
.AddAspNetCoreInstrumentation()
|
|
||||||
.AddHttpClientInstrumentation()
|
|
||||||
.AddOtlpExporter(opt =>
|
|
||||||
{
|
|
||||||
opt.Endpoint = new Uri(endpoint);
|
|
||||||
opt.Protocol = OtlpExportProtocol.Grpc;
|
|
||||||
opt.Headers = otlpHeaders;
|
|
||||||
}))
|
|
||||||
.WithMetrics(metrics => metrics
|
|
||||||
.SetResourceBuilder(resourceBuilder)
|
|
||||||
.AddMeter(serviceName)
|
|
||||||
.AddAspNetCoreInstrumentation()
|
|
||||||
.AddRuntimeInstrumentation()
|
|
||||||
.AddOtlpExporter(opt =>
|
|
||||||
{
|
|
||||||
opt.Endpoint = new Uri(endpoint);
|
|
||||||
opt.Protocol = OtlpExportProtocol.Grpc;
|
|
||||||
opt.Headers = otlpHeaders;
|
|
||||||
}));
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
|
||||||
using PostFundManagement.Domain.Quartz;
|
|
||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Quartz
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddQuartzSchedulerClient(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
|
||||||
|
|
||||||
services.ConfigureCommon();
|
|
||||||
|
|
||||||
services.AddQuartz(config =>
|
|
||||||
{
|
|
||||||
config.SchedulerName = schedulerName;
|
|
||||||
config.SchedulerId = "AUTO";
|
|
||||||
|
|
||||||
config.UseSimpleTypeLoader();
|
|
||||||
config.UseDefaultThreadPool(options => options.MaxConcurrency = 0);
|
|
||||||
config.UseTimeZoneConverter();
|
|
||||||
|
|
||||||
config.UsePersistentStore(storage =>
|
|
||||||
{
|
|
||||||
storage.PerformSchemaValidation = false;
|
|
||||||
|
|
||||||
storage.UseSystemTextJsonSerializer();
|
|
||||||
storage.SetProperty("quartz.jobStore.clustered", "true");
|
|
||||||
|
|
||||||
storage.UsePostgres(options =>
|
|
||||||
{
|
|
||||||
options.ConnectionString = connectionString!;
|
|
||||||
options.TablePrefix = "quartz.qrtz_";
|
|
||||||
});
|
|
||||||
storage.UseClustering(cluster =>
|
|
||||||
{
|
|
||||||
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
|
||||||
cluster.CheckinMisfireThreshold = TimeSpan.FromSeconds(90);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddQuartzScheduler(this IServiceCollection services, string schedulerName, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var connectionString = configuration.GetConnectionString(DatabaseConfigName);
|
|
||||||
|
|
||||||
services.ConfigureCommon();
|
|
||||||
|
|
||||||
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
|
|
||||||
|
|
||||||
services.AddQuartz(config =>
|
|
||||||
{
|
|
||||||
config.SchedulerName = schedulerName;
|
|
||||||
config.SchedulerId = "AUTO";
|
|
||||||
config.InterruptJobsOnShutdown = true;
|
|
||||||
config.InterruptJobsOnShutdownWithWait = true;
|
|
||||||
config.MaxBatchSize = 5;
|
|
||||||
|
|
||||||
config.UseSimpleTypeLoader();
|
|
||||||
config.UseDefaultThreadPool(options => options.MaxConcurrency = 1);
|
|
||||||
config.UseTimeZoneConverter();
|
|
||||||
|
|
||||||
config.SetProperty("quartz.jobStore.misfireThreshold", TimeSpan.FromMinutes(2).TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
|
|
||||||
|
|
||||||
config.UsePersistentStore(storage =>
|
|
||||||
{
|
|
||||||
storage.PerformSchemaValidation = false;
|
|
||||||
|
|
||||||
storage.UseSystemTextJsonSerializer();
|
|
||||||
storage.SetProperty("quartz.jobStore.clustered", "true");
|
|
||||||
|
|
||||||
storage.UsePostgres(options =>
|
|
||||||
{
|
|
||||||
options.ConnectionString = connectionString!;
|
|
||||||
options.TablePrefix = "quartz.qrtz_";
|
|
||||||
});
|
|
||||||
storage.UseClustering(cluster =>
|
|
||||||
{
|
|
||||||
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
|
||||||
cluster.CheckinMisfireThreshold = TimeSpan.FromSeconds(90);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IServiceCollection ConfigureCommon(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.Configure<QuartzOptions>(options =>
|
|
||||||
{
|
|
||||||
options.Scheduling.IgnoreDuplicates = true;
|
|
||||||
options.Scheduling.OverWriteExistingData = true;
|
|
||||||
|
|
||||||
options["quartz.plugin.jobHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz.Plugins";
|
|
||||||
options["quartz.plugin.triggerHistory.type"] = "Quartz.Plugin.History.LoggingTriggerHistoryPlugin, Quartz.Plugins";
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddTransient<RetryJobListener>();
|
|
||||||
services.AddTransient<IJobOrchestrator, JobOrchestrator>();
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using PostFundManagement.Domain.Abstractions;
|
|
||||||
using PostFundManagement.Domain.Services;
|
|
||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class S3
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddGarageS3(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{GeneralS3SettingsSection}:ServiceUrl").Value))
|
|
||||||
{
|
|
||||||
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(GeneralBucketName, (provider, client) =>
|
|
||||||
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{GeneralS3SettingsSection}:AccessKey").Value,
|
|
||||||
configuration.GetSection($"{GeneralS3SettingsSection}:SecretKey").Value),
|
|
||||||
new AmazonS3Config
|
|
||||||
{
|
|
||||||
ServiceURL = configuration.GetSection($"{GeneralS3SettingsSection}:ServiceUrl").Value,
|
|
||||||
AuthenticationRegion = configuration.GetSection($"{GeneralS3SettingsSection}:Region").Value,
|
|
||||||
ForcePathStyle = true,
|
|
||||||
EndpointDiscoveryEnabled = true,
|
|
||||||
UseHttp = configuration.GetSection($"{GeneralS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
|
|
||||||
}));
|
|
||||||
|
|
||||||
services.AddKeyedScoped<IS3Service, GeneralS3Service>(GeneralBucketName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value))
|
|
||||||
{
|
|
||||||
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(EvidenceS3SettingsSection, (provider, client) =>
|
|
||||||
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{EvidenceS3SettingsSection}:AccessKey").Value,
|
|
||||||
configuration.GetSection($"{EvidenceS3SettingsSection}:SecretKey").Value),
|
|
||||||
new AmazonS3Config
|
|
||||||
{
|
|
||||||
ServiceURL = configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value,
|
|
||||||
AuthenticationRegion = configuration.GetSection($"{EvidenceS3SettingsSection}:Region").Value,
|
|
||||||
ForcePathStyle = true,
|
|
||||||
EndpointDiscoveryEnabled = true,
|
|
||||||
UseHttp = configuration.GetSection($"{EvidenceS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
|
|
||||||
}));
|
|
||||||
|
|
||||||
services.AddKeyedScoped<IS3Service, EvidenceS3Service>(EvidenceS3SettingsSection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value))
|
|
||||||
{
|
|
||||||
services.AddKeyedSingleton<IAmazonS3, AmazonS3Client>(ContractS3SettingsSection, (provider, client) =>
|
|
||||||
new AmazonS3Client(new BasicAWSCredentials(configuration.GetSection($"{ContractS3SettingsSection}:AccessKey").Value,
|
|
||||||
configuration.GetSection($"{ContractS3SettingsSection}:SecretKey").Value),
|
|
||||||
new AmazonS3Config
|
|
||||||
{
|
|
||||||
ServiceURL = configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value,
|
|
||||||
AuthenticationRegion = configuration.GetSection($"{ContractS3SettingsSection}:Region").Value,
|
|
||||||
ForcePathStyle = true,
|
|
||||||
EndpointDiscoveryEnabled = true,
|
|
||||||
UseHttp = configuration.GetSection($"{ContractS3SettingsSection}:ServiceUrl").Value!.Contains("http://", StringComparison.InvariantCultureIgnoreCase),
|
|
||||||
}));
|
|
||||||
|
|
||||||
services.AddKeyedScoped<IS3Service, ContractS3Service>(ContractS3SettingsSection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
public static class Timezones
|
|
||||||
{
|
|
||||||
public static TimeZoneInfo SouthAfricanTimeZone => TimeZoneInfo.FindSystemTimeZoneById("South Africa Standard Time");
|
|
||||||
|
|
||||||
public static string? LocaliseDateTime(this DateTime dateTime, TimeSpan offset) => offset.Hours > 0
|
|
||||||
? $"{dateTime:yyyy-MM-ddTHH:mm:ss.fff}+{offset.Hours:00}:{offset.Minutes:00}"
|
|
||||||
: $"{dateTime:yyyy-MM-ddTHH:mm:ss.fff}{offset.Hours:00}:{offset.Minutes:00}";
|
|
||||||
|
|
||||||
public static string? LocaliseDateTimeOffset(this DateTimeOffset dateTime, TimeSpan offset) => LocaliseDateTime(dateTime.DateTime, offset);
|
|
||||||
|
|
||||||
public static DateTimeOffset ToDateTimeWithTimeZone(this DateTime source, TimeZoneInfo? timezone = null)
|
|
||||||
{
|
|
||||||
DateTime sourceDateAdjusted = source.Kind != DateTimeKind.Utc
|
|
||||||
? new(source.Ticks, DateTimeKind.Utc)
|
|
||||||
: source;
|
|
||||||
|
|
||||||
var localised = timezone is null
|
|
||||||
? new DateTimeOffset(sourceDateAdjusted.Ticks, SouthAfricanTimeZone.BaseUtcOffset).LocaliseDateTimeOffset(SouthAfricanTimeZone.BaseUtcOffset)
|
|
||||||
: new DateTimeOffset(sourceDateAdjusted.Ticks, timezone!.BaseUtcOffset).LocaliseDateTimeOffset(timezone.BaseUtcOffset);
|
|
||||||
|
|
||||||
return DateTimeOffset.Parse(localised!, CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DateTime UtcNow(this TimeZoneInfo timezone) => ToDateTimeWithTimeZone(DateTime.Now, timezone).UtcDateTime;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Health;
|
|
||||||
|
|
||||||
public sealed class PostgresHealthCheck(IConfiguration configuration) : IHealthCheck
|
|
||||||
{
|
|
||||||
private readonly string connectionString = configuration.GetConnectionString(DatabaseConfigName)!;
|
|
||||||
|
|
||||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await using var dataSource = NpgsqlDataSource.Create(connectionString);
|
|
||||||
await using var connection = await dataSource.OpenConnectionAsync(cancellationToken);
|
|
||||||
|
|
||||||
await using var command = connection.CreateCommand();
|
|
||||||
command.CommandText = "SELECT 1";
|
|
||||||
|
|
||||||
await command.ExecuteScalarAsync(cancellationToken);
|
|
||||||
|
|
||||||
return HealthCheckResult.Healthy($"{DatabaseConfigName} is responsive.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return HealthCheckResult.Unhealthy($"{DatabaseConfigName} is unreachable.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using static PostFundManagement.Domain.Extensions.Constants;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Health;
|
|
||||||
|
|
||||||
public sealed class QuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck
|
|
||||||
{
|
|
||||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var scheduler = await schedulerFactory.GetScheduler(DefaultSchedulerName, cancellationToken);
|
|
||||||
|
|
||||||
if(scheduler == null)
|
|
||||||
return HealthCheckResult.Unhealthy($"Scheduler with name '{DefaultSchedulerName}' not found.");
|
|
||||||
|
|
||||||
if (!scheduler.IsStarted)
|
|
||||||
return HealthCheckResult.Unhealthy($"{DefaultSchedulerName} Quartz scheduler is not running");
|
|
||||||
|
|
||||||
await scheduler.CheckExists(new JobKey(Guid.NewGuid().ToString()), cancellationToken);
|
|
||||||
|
|
||||||
return HealthCheckResult.Healthy($"{DefaultSchedulerName} Quartz scheduler is ready");
|
|
||||||
}
|
|
||||||
catch (SchedulerException)
|
|
||||||
{
|
|
||||||
return HealthCheckResult.Unhealthy($"{DefaultSchedulerName} Quartz scheduler cannot connect to the store");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Mediator;
|
|
||||||
|
|
||||||
public sealed class LoggingPipelineBehavior<TRequest, TResponse>(ILogger<LoggingPipelineBehavior<TRequest, TResponse>> logger) :
|
|
||||||
IPipelineBehavior<TRequest, TResponse?>
|
|
||||||
where TRequest : IRequest<TResponse>
|
|
||||||
where TResponse : ResultBase, new()
|
|
||||||
{
|
|
||||||
public async ValueTask<TResponse?> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse?> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
TResponse? response = await next(message, cancellationToken);
|
|
||||||
|
|
||||||
if (response is null)
|
|
||||||
logger.LogCritical("{Request} {TypeName} was returned as null", typeof(TRequest).Name, typeof(TRequest).Name);
|
|
||||||
|
|
||||||
if(response?.IsFailed == true || response?.Errors?.Any() == true)
|
|
||||||
{
|
|
||||||
foreach (var error in response.Errors)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(error.Message))
|
|
||||||
logger.LogWarning("{Request} {Error}", typeof(TRequest).Name, error.Message);
|
|
||||||
|
|
||||||
if (error?.Reasons?.Count > 0)
|
|
||||||
error.Reasons.ForEach(r => logger.LogError("{Request} {Reason}", typeof(TRequest).Name, r.ToString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using PostFundManagement.Domain.Extensions;
|
|
||||||
|
|
||||||
namespace PostFundManagement.Domain.Mediator;
|
|
||||||
|
|
||||||
public sealed class TelemetryPipelineBehavior<TRequest, TResponse> :
|
|
||||||
IPipelineBehavior<TRequest, TResponse?>
|
|
||||||
where TRequest : IRequest<TResponse>
|
|
||||||
where TResponse : ResultBase, new()
|
|
||||||
{
|
|
||||||
public async ValueTask<TResponse?> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse?> next, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var requestName = typeof(TRequest).Name;
|
|
||||||
|
|
||||||
using var activity = MediatorTelemetry.Source.StartActivity(requestName);
|
|
||||||
|
|
||||||
activity?.SetTag("mediator.request_type", typeof(TRequest).FullName);
|
|
||||||
|
|
||||||
var stopWatch = Stopwatch.StartNew();
|
|
||||||
var status = "Success";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TResponse? response = await next(message, cancellationToken);
|
|
||||||
|
|
||||||
if (response is null)
|
|
||||||
{
|
|
||||||
status = "NullResponse";
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, "Response was null");
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.IsFailed)
|
|
||||||
{
|
|
||||||
status = "Failed";
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, "Request failed");
|
|
||||||
|
|
||||||
var firstError = response.Errors.FirstOrDefault()?.Message ?? "Unknown Error";
|
|
||||||
activity?.SetTag("error.message", firstError);
|
|
||||||
|
|
||||||
foreach (var error in response.Errors)
|
|
||||||
activity?.AddEvent(new ActivityEvent("Result Error", tags: new() { { "message", error.Message } }));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Ok);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
status = "Exception";
|
|
||||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
|
||||||
|
|
||||||
activity?.AddException(ex);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
stopWatch.Stop();
|
|
||||||
|
|
||||||
var tags = new TagList { { "request", requestName }, { "status", status } };
|
|
||||||
|
|
||||||
MediatorTelemetry.RequestCounter.Add(1, tags);
|
|
||||||
MediatorTelemetry.RequestDuration.Record(stopWatch.Elapsed.TotalMilliseconds, tags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Models;
|
|
||||||
|
|
||||||
public class Activity
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
public long? ProjectId { get; set; }
|
|
||||||
|
|
||||||
public long? MilestoneId { get; set; }
|
|
||||||
|
|
||||||
public Guid CreatedBy { get; set; }
|
|
||||||
|
|
||||||
public Guid? UpdatedBy { get; set; }
|
|
||||||
|
|
||||||
public Guid? ApprovedBy { get; set; }
|
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
public string? Category { get; set; }
|
|
||||||
|
|
||||||
public ApprovalStatus Status { get; set; }
|
|
||||||
|
|
||||||
public decimal Amount { get; set; }
|
|
||||||
|
|
||||||
public string? Title { get; set; }
|
|
||||||
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
public DateTime? PerformedAt { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Models;
|
|
||||||
|
|
||||||
public class AuditLog
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
public string? EntityName { get; set; }
|
|
||||||
|
|
||||||
public long EntityId { get; set; }
|
|
||||||
|
|
||||||
public string? Action { get; set; }
|
|
||||||
|
|
||||||
public string[]? Changes { get; set; }
|
|
||||||
|
|
||||||
public Guid PerformedBy { get; set; }
|
|
||||||
|
|
||||||
public DateTime Timestamp { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace PostFundManagement.Domain.Models;
|
|
||||||
|
|
||||||
public class Award
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
public long OrganisationId { get; set; }
|
|
||||||
|
|
||||||
public long ProgrammeId { get; set; }
|
|
||||||
|
|
||||||
public long? ProjectId { get; set; }
|
|
||||||
|
|
||||||
public Guid CreatedBy { get; set; }
|
|
||||||
|
|
||||||
public Guid? UpdatedBy { get; set; }
|
|
||||||
|
|
||||||
public Guid? ApprovedBy { get; set; }
|
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
|
|
||||||
public DateTime? UpdatedAt { get; set; }
|
|
||||||
|
|
||||||
public ApprovalStatus Status { get; set; }
|
|
||||||
|
|
||||||
public decimal Amount { get; set; }
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user