Basic project setup #14
+115
@@ -0,0 +1,115 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: build
|
||||
|
||||
steps:
|
||||
- name: dotnet-build
|
||||
image: mcr.microsoft.com/dotnet/sdk:10.0
|
||||
commands:
|
||||
- dotnet restore MidrandBooks.slnx
|
||||
- dotnet build MidrandBooks.slnx -c Release
|
||||
|
||||
- name: dotnet-test
|
||||
image: mcr.microsoft.com/dotnet/sdk:10.0
|
||||
commands:
|
||||
- dotnet restore MidrandBooks.slnx
|
||||
- dotnet test MidrandBooks.slnx -c Release --no-restore
|
||||
|
||||
trigger:
|
||||
event: [ pull_request ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: package
|
||||
|
||||
steps:
|
||||
- name: docker-build
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: nexus.khongisa.co.za
|
||||
repo: nexus.khongisa.co.za/midrandbooks
|
||||
tags: [ latest, "1.${DRONE_BUILD_NUMBER}" ]
|
||||
custom_labels:
|
||||
- org.opencontainers.image.source=https://gitea.khongisa.co.za/litecharms/midrandbooks
|
||||
- org.opencontainers.image.version=1.${DRONE_BUILD_NUMBER}
|
||||
- org.opencontainers.image.revision=${DRONE_COMMIT_SHA}
|
||||
username: { from_secret: docker_username }
|
||||
password: { from_secret: docker_password }
|
||||
|
||||
- name: gitea-tag-release
|
||||
image: alpine/git
|
||||
environment:
|
||||
GITEA_TOKEN: { from_secret: git_token }
|
||||
GITEA_USER: { from_secret: git_username }
|
||||
GITEA_PASS: { from_secret: git_password }
|
||||
commands:
|
||||
- echo "169.255.58.144 gitea.khongisa.co.za" >> /etc/hosts
|
||||
- apk add --no-cache curl
|
||||
- git remote set-url origin https://$${GITEA_USER}:$${GITEA_PASS}@gitea.khongisa.co.za/litecharms/midrandbooks.git
|
||||
- git tag 1.${DRONE_BUILD_NUMBER}
|
||||
- git push origin 1.${DRONE_BUILD_NUMBER}
|
||||
- |
|
||||
curl -X POST "https://gitea.khongisa.co.za/api/v1/repos/litecharms/midrandbooks/releases" \
|
||||
-H "Authorization: token $${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"tag_name\": \"1.${DRONE_BUILD_NUMBER}\",
|
||||
\"target_commitish\": \"${DRONE_COMMIT_SHA}\",
|
||||
\"name\": \"Release 1.${DRONE_BUILD_NUMBER}\",
|
||||
\"body\": \"### Artifacts\n* **Docker Image:** nexus.khongisa.co.za/midrandbooks:1.${DRONE_BUILD_NUMBER}\n* **NuGet:** [View on Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\",
|
||||
\"draft\": false,
|
||||
\"prerelease\": false
|
||||
}"
|
||||
|
||||
depends_on:
|
||||
- build
|
||||
|
||||
trigger:
|
||||
event: [ pull_request ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: uat
|
||||
|
||||
steps:
|
||||
- name: deploy
|
||||
image: bitnami/kubectl:latest
|
||||
environment:
|
||||
KUBE_CONFIG: { from_secret: kube_config }
|
||||
commands:
|
||||
- mkdir -p $HOME/.kube
|
||||
- echo "$KUBE_CONFIG" > $HOME/.kube/config
|
||||
- kubectl apply -f midrandbooks-uat.yml
|
||||
- sleep 10
|
||||
- kubectl rollout restart deployment/midrandbooks -n midrandbooks-uat
|
||||
|
||||
depends_on:
|
||||
- package
|
||||
|
||||
trigger:
|
||||
event: [ pull_request ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: prod
|
||||
|
||||
steps:
|
||||
- name: deploy
|
||||
image: bitnami/kubectl:latest
|
||||
environment:
|
||||
KUBE_CONFIG: { from_secret: kube_config }
|
||||
commands:
|
||||
- mkdir -p $HOME/.kube
|
||||
- echo "$KUBE_CONFIG" > $HOME/.kube/config
|
||||
- kubectl apply -f midrandbooks.yml
|
||||
|
||||
depends_on:
|
||||
- uat
|
||||
|
||||
trigger:
|
||||
event: [ promote ]
|
||||
target: [ production ]
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### 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
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"root": true,
|
||||
"dependentFileProviders": {
|
||||
"add": {
|
||||
"addedExtension": {},
|
||||
"extensionToExtension": {
|
||||
"add": {
|
||||
".css": [ ".razor" ],
|
||||
".cs": [ ".razor" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /app
|
||||
|
||||
COPY ["nuget.config", "./"]
|
||||
COPY ["MidrandBookshop/MidrandBookshop.csproj", "MidrandBookshop/"]
|
||||
RUN dotnet restore "MidrandBookshop/MidrandBookshop.csproj" --configfile nuget.config
|
||||
COPY . .
|
||||
RUN dotnet publish "MidrandBookshop/MidrandBookshop.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
|
||||
WORKDIR /app
|
||||
|
||||
EXPOSE 8080
|
||||
EXPOSE 8081
|
||||
|
||||
COPY --from=build /app/publish .
|
||||
|
||||
ENTRYPOINT ["dotnet", "MidrandBookshop.dll"]
|
||||
@@ -0,0 +1,18 @@
|
||||
PROPRIETARY LICENSE
|
||||
|
||||
Copyright (c) 2026 Lite Charms (PTY) Ltd. All rights reserved.
|
||||
|
||||
This software and its associated documentation (the "Software") are the
|
||||
proprietary property of Lite Charms (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,3 +1,9 @@
|
||||
<Solution>
|
||||
<Folder Name="/Solution Items/">
|
||||
<File Path=".drone.yml" />
|
||||
<File Path="Dockerfile" />
|
||||
<File Path="midrandbooks-uat.yml" />
|
||||
<File Path="README.md" />
|
||||
</Folder>
|
||||
<Project Path="MidrandBookshop/MidrandBookshop.csproj" />
|
||||
</Solution>
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||
<h2>Midrand Books</h2>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
@@ -15,9 +11,3 @@
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui" data-nosnippet>
|
||||
An unhandled error has occurred.
|
||||
<a href="." class="reload">Reload</a>
|
||||
<span class="dismiss">🗙</span>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">MidrandBookshop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="nav flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="counter">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="weather">
|
||||
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
.navbar-toggler {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
width: 3.5rem;
|
||||
height: 2.5rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.navbar-toggler:checked {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
min-height: 3.5rem;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.bi {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link {
|
||||
color: #d7d7d7;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-toggler:checked ~ .nav-scrollable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
@page "/counter"
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<PageTitle>Counter</PageTitle>
|
||||
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p role="status">Current count: @currentCount</p>
|
||||
|
||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||
|
||||
@code {
|
||||
private int currentCount = 0;
|
||||
|
||||
private void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/"
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
<PageTitle>Midrand Books</PageTitle>
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
<h3>midrandbooks.co.za</h3>
|
||||
|
||||
Welcome to your new app.
|
||||
Welcome to the Midrand Books online bookstore! We are passionate about providing a wide selection of books to readers of all ages and interests. Whether you're looking for the latest bestsellers, classic literature, or educational resources, we have something for everyone.
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
@page "/weather"
|
||||
@attribute [StreamRendering]
|
||||
|
||||
<PageTitle>Weather</PageTitle>
|
||||
|
||||
<h1>Weather</h1>
|
||||
|
||||
<p>This component demonstrates showing data.</p>
|
||||
|
||||
@if (forecasts == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th aria-label="Temperature in Celsius">Temp. (C)</th>
|
||||
<th aria-label="Temperature in Fahrenheit">Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
// Simulate asynchronous loading to demonstrate streaming rendering
|
||||
await Task.Delay(500);
|
||||
|
||||
var startDate = DateOnly.FromDateTime(DateTime.Now);
|
||||
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
|
||||
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = startDate.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = summaries[Random.Shared.Next(summaries.Length)]
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
public int TemperatureC { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||
<UserSecretsId>63d55865-dbfb-40d6-b6df-9bf6d14d15ca</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
{
|
||||
"Email": {
|
||||
"Credentials": {
|
||||
"Username": "shop@litecharms.co.za"
|
||||
},
|
||||
"Port": 465,
|
||||
"Host": "mail.litecharms.co.za",
|
||||
"UseSsl": true
|
||||
},
|
||||
"Monitoring": {
|
||||
"ApiKey": "",
|
||||
"Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889",
|
||||
"ServiceName": "LiteCharms.Shop"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.EntityFrameworkCore": "Error"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
|
||||
@@ -1 +1,78 @@
|
||||
# MidrandBooks
|
||||
# Midrand Books Ecommerce Platform
|
||||
|
||||
An online bookstore web application designed for Midrand Books, allowing local and national customers to browse, search, and purchase books seamlessly.
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
* **User Authentication**: Secure customer registration, login, and profile management.
|
||||
* **Product Catalog**: Advanced search, filtering by genre, and dynamic book categorization.
|
||||
* **Shopping Cart**: Real-time cart updates, item persistence, and coupon code validation.
|
||||
* **Secure Checkout**: Integrated payment gateways supporting local South African methods (e.g., PayFast, Ozow, or Yoco).
|
||||
* **Order Tracking**: Automated email confirmations and live delivery status tracking.
|
||||
* **Admin Dashboard**: Content management system (CMS) to manage inventory, track sales, and process orders.
|
||||
|
||||
## 🛠️ Tech Stack
|
||||
|
||||
* **Frontend**: Blazor / HTML5 / Bootstrap CSS
|
||||
* **Backend**: Blazor Server
|
||||
* **Database**: PostgreSQL
|
||||
* **Payment Gateway**: PayFast API / PayPal SDK
|
||||
|
||||
## 📋 Prerequisites
|
||||
|
||||
Before setting up the project locally, ensure you have the following installed:
|
||||
|
||||
* .NET SDK (v6.0 or higher)
|
||||
* Git
|
||||
|
||||
## 🔧 Installation & Setup
|
||||
|
||||
Follow these steps to run the project locally.
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone https://gitea.khongisa.co.za/litecharms/midrandbooks.git
|
||||
cd midrandbooks
|
||||
```
|
||||
|
||||
2. **Install dependencies**
|
||||
```bash
|
||||
# restore NuGet dependencies
|
||||
dotnet restore
|
||||
```
|
||||
|
||||
3. **Environment Variables**
|
||||
Create a `.env` file in both the frontend and backend roots. Use the `.env.example` files provided as a reference.
|
||||
```env
|
||||
DATABASE_URL=your_database_url
|
||||
Email = mailbox settings
|
||||
Monitoring = Aspire dashboard settings
|
||||
PAYMENT_GATEWAY_KEY=your_api_key
|
||||
```
|
||||
|
||||
4. **Run the application**
|
||||
```bash
|
||||
# Start app
|
||||
dotnet run
|
||||
```
|
||||
|
||||
5. **Access the app**
|
||||
Open your browser and navigate to `http://localhost:5000`.
|
||||
|
||||
## 📦 Deployment
|
||||
|
||||
* **Frontend**: Hosted on Lite Charms Cloud.
|
||||
* **Backend**: Hosted on Lite Charms Cloud.
|
||||
* **Database**: Managed on Lite Charms PVC LXC container settings
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a feature branch: `git checkout -b feature/NewFeature`.
|
||||
3. Commit your changes: `git commit -m 'Add NewFeature'`.
|
||||
4. Push to the branch: `git push origin feature/NewFeature`.
|
||||
5. Open a Pull Request.
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: midrandbooks-uat
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: midrandbooks-config
|
||||
namespace: midrandbooks-uat
|
||||
data:
|
||||
ASPNETCORE_ENVIRONMENT: "Development"
|
||||
ASPNETCORE_URLS: "http://0.0.0.0:8080"
|
||||
Monitoring__Address: "http://aspire-dashboard-service.aspire.svc.cluster.local:18889"
|
||||
Monitoring__ServiceName: "MidrandBooks.Uat"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: midrandbooks-secrets
|
||||
namespace: midrandbooks-uat
|
||||
type: Opaque
|
||||
data:
|
||||
connection-string: SG9zdD0xOTIuMTY4LjEuMTcwO0RhdGFiYXNlPW1pZHJhbmRib29rcy1kZXY7VXNlcm5hbWU9bWlkcmFuZGJvb2tzLWRldi11c2VyO1Bhc3N3b3JkPVBCd1pnbzZRS3dHN1ZGO1BlcnNpc3QgU2VjdXJpdHkgSW5mbz1UcnVl
|
||||
connection-string-quartz: SG9zdD0xOTIuMTY4LjEuMTcwO0RhdGFiYXNlPXNjaGVkdWxlci1kZXY7VXNlcm5hbWU9c2NoZWR1bGVyLWRldi11c2VyO1Bhc3N3b3JkPWtWVm1vV0tKM3h6Z1FYO1BlcnNpc3QgU2VjdXJpdHkgSW5mbz1UcnVl
|
||||
aspire-apikey: bWMzRzYzSzJqNVpPRXNpMEFqTW9qTFRYbTFLRVpGY3R6SUlqU3dEaVRHdXQ4cUdTa1B1V3d4R1AxUmJzY0pVbw==
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: midrandbooks-pvc
|
||||
namespace: midrandbooks-uat
|
||||
spec:
|
||||
accessModes: ["ReadWriteMany"]
|
||||
storageClassName: nfs-storage
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: midrandbooks
|
||||
namespace: midrandbooks-uat
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: midrandbooks
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: midrandbooks
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: DoesNotExist
|
||||
containers:
|
||||
- name: midrandbooks
|
||||
image: nexus.khongisa.co.za/midrandbooks:latest
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: midrandbooks-config
|
||||
env:
|
||||
- name: ConnectionStrings__PostgresScheduler
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: midrandbooks-secrets
|
||||
key: connection-string-quartz
|
||||
- name: ConnectionStrings__PostgresMidrandshop
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: midrandbooks-secrets
|
||||
key: connection-string
|
||||
- name: Monitoring__Address
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: midrandbooks-config
|
||||
key: Monitoring__Address
|
||||
- name: Monitoring__ServiceName
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: midrandbooks-config
|
||||
key: Monitoring__ServiceName
|
||||
- name: Monitoring__ApiKey
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: midrandbooks-secrets
|
||||
key: aspire-apikey
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /app/wwwroot/content
|
||||
resources:
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 5
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: midrandbooks-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: midrandbooks-service
|
||||
namespace: midrandbooks-uat
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: midrandbooks
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: midrandbooks-web-secure
|
||||
namespace: midrandbooks-uat
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`midrandbooks.co.za`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: midrandbooks-service
|
||||
port: 80
|
||||
sticky:
|
||||
cookie:
|
||||
name: "lp-sticky-session"
|
||||
httpOnly: true
|
||||
secure: true
|
||||
tls: {}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="KhongisaNexus" value="https://nexus.khongisa.co.za/repository/nuget-group/index.json" />
|
||||
</packageSources>
|
||||
|
||||
<packageSourceCredentials>
|
||||
<KhongisaNexus>
|
||||
<add key="Username" value="api-key" />
|
||||
<add key="ClearTextPassword" value="4a285231-5a49-334f-a216-7c2e63418e5c" />
|
||||
</KhongisaNexus>
|
||||
</packageSourceCredentials>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user