Skip to content

deeebeee3/Narsties

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Car Auction App (Narsties) .NET APP / NEXTJS

2 Auctions Service

git log --all --decorate --oneline --graph

git checkout 5b3f518

git checkout main


ADD A GIT ALIAS:

git config --global alias.adog "log --all --decorate --oneline --graph"

git adog


DOTNET

dotnet --version

dotnet --info

dotnet new list


[create new .NET solution] dotnet new sln

[create new .NET Auction Service] dotnet new webapi -o src/AuctionService

[add the Auction Service to the solution we created] dotnet sln add src/AuctionService


code .


[Start the project in watch mode] dotnet watch

[Use CLI to install tools] dotnet tool list -g

dotnet tool install dotnet-ef -g

[Get latest version of a tool] dotnet tool update dotnet-ef -g


CREATE A NEW MIGRATION

The following command will create a migration that will set up the database schema based on the code we have written and the code in the AuctionDBContext class...

dotnet ef migrations add "InitialCreate" -o Data/Migrations


DOCKER

Get Postgres db service running on Docker based on the config in the .yaml file

docker compose up -d


UPDATE THE DB

NOW THAT DATABASE RUNNING we can we can create the tables inside it based on the migration we created above:

dotnet ef database update


We added some SEED Data to the DBInitializer class... lets drop current db and create again with dotnet watch....

dotnet ef database drop

dotnet watch


GIT

(first cd to project root: /Users/deepakbhari/Projects/Narsties)

git init

dotnet new gitignore

--

git add .

git add -A :/

git commit -m "Finished section 2"

Then in github create a new repository.... then ...

git remote add origin git@github.com:deeebeee3/Narsties.git

git push origin main

3 Search Service

~/Projects/Narsties ->

dotnet new webapi -o src/SearchService

Make SearchService appear in Solution Explorer:

dotnet sln add src/SearchService


cd SearchService

dootnet build

dotnet watch


https://mongodb-entities.com/wiki/Code-Samples.html#queries


docker compose down

docker compose up -d


4 RabbitMQ

rabbitmq: image: rabbitmq:3-management-alpine ports: - 5672:5672 - 15672:15672

http://localhost:15672/

un/pw: guest guest


https://masstransit.io/


NuGet package manager install MassTransit.RabbitMQ by Chris Patterson


/Users/deepakbhari/Projects/Narsties

dotnet new classlib -o src/src/Contracts

dotnet sln add src/Contracts


cd src/AuctionService dotnet add reference ../../src/Contracts

cd src/SearchService dotnet add reference ../../src/Contracts


NuGet package manager install MassTransit.REntityFrameworkCore by Chris Patterson inside the AuctionService...

dotnet ef migrations add Outbox


5 Identity Server

https://docs.duendesoftware.com/identityserver/v6/quickstarts/0_overview/


dotnet new --install Duende.IdentityServer.Templates


dotnet new isaspid -o src/IdentityService

when prompted to seed users - choose No (we do this ourself later)

dotnet sln add src/IdentityService


change from https to http....

src/IdentityService/Properties/launchSettings.json

"profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:5000" } } }


Delete the original migrations folder - as it was created for sqlite database and wont work with our postgres database...

dotnet ef migrations add "InitialCreate" -o Data/Migrations


might not need to do the following (only if you get a message in terminal about outdated tools) :

dotnet tool update dotnet-ef -g


copy postgres connection string from auction service and replace one in identity server service with it:

{ "Serilog": { "MinimumLevel": { "Default": "Debug", "Override": { "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", "Microsoft.AspNetCore.Authentication": "Debug", "System": "Warning" } } },

"ConnectionStrings": { "DefaultConnection": "Server=localhost:5432;User Id=postgres;Password=postgrespw;Database=identity" } }


http://localhost:5000/Account/Login?ReturnUrl=%2Fdiagnostics

bob / Pass123$


Might need to restart app after making changes to Razor pages to make them visible...

hot reload not working properly


jwt.io


6 Gateway Service

dotnet new web -o src/GatewayService

dotnet sln add src/GatewayService


NuGet packages to install:

Yarp.ReverseProxy by Microsoft

Microsoft.AspNetCore.Authentication.JwtBearer by Microsoft


Yarp Docs

https://microsoft.github.io/reverse-proxy/

https://microsoft.github.io/reverse-proxy/articles/config-files.html

"ReverseProxy": { "Routes": { "route1" : { "ClusterId": "cluster1", "Match": { "Path": "{**catch-all}", "Hosts" : [ "www.aaaaa.com", "www.bbbbb.com"], }, } }, "Clusters": { "cluster1": { "Destinations": { "cluster1/destination1": { "Address": "https://example.com/" } } } } }


7 Dockerizing our application

Install the VSCode Docker extension


src/AuctionService/Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:8.0 as build WORKDIR /app EXPOSE 80

copy all .csproj files and restore as distinct layers. Use of the same COPY command

# for every dockerfile in the project to take advantage of docker caching

COPY Narsties.sln Narsties.sln COPY src/AuctionService/AuctionService.csproj src/AuctionService/AuctionService.csproj COPY src/SearchService/SearchService.csproj src/SearchService/SearchService.csproj COPY src/GatewayService/GatewayService.csproj src/GatewayService/GatewayService.csproj COPY src/Contracts/Contracts.csproj src/Contracts/Contracts.csproj COPY src/IdentityService/IdentityService.csproj src/IdentityService/IdentityService.csproj

Restore package deps

RUN dotnet restore Narsties.sln

Copy the app folders over

COPY src/AuctionService src/AuctionService COPY src/Contracts src/Contracts

WORKDIR /app/src/AuctionService

RUN dotnet publish -c Release -o /app/src/out

Build runtime image

FROM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app COPY --from=build /app/src/out . ENTRYPOINT [ "dotnet", "AuctionService.dll" ]


build the docker image for the auction service

docker build -f src/AuctionService/Dockerfile -t testing123 .


take the image and spin up a container

(this won't work yet - we need to do some extra configuration) Also we don't want to do it this way - we want to use docker compose to start our containers

docker run testing123


docker-compose.yaml:

auction-svc: image: deepakbhari/auction-svc:latest build: context: . dockerfile: src/AuctionService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:80 - RabbitMqHost=rabbitmq - ConnectionStringsDefaultConnection=Server=postgres:5432;User Id=postgres;Password=postgrespw;Database=auctions - IdentityServiceUrl=http://identity-svc ports: - 7001:80 depends_on: - postgres - rabbitmq


docker compose build auction-svc

docker compose up -d

our auction-svc will be running as a container and it should be connecting to postgres and rabbitmq...


docker-compose.yaml:

search-svc: image: deepakbhari/search-svc:latest build: context: . dockerfile: src/SearchService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:80 - RabbitMqHost=rabbitmq - ConnectionStringsMongoDbConnection=mongodb://root:mongopw@mongodb - AuctionServiceUrl=http://auction-svc ports: - 7002:80 depends_on: - mongodb - rabbitmq


docker compose build search-svc

docker compose up -d


docker-compose.yaml:

identity-svc: image: deepakbhari/identity-svc:latest build: context: . dockerfile: src/IdentityService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:80 - ConnectionStrings__DefaultConnection=Server=postgres:5432;User Id=postgres;Password=postgrespw;Database=identity ports: - 5000:80 depends_on: - postgres


docker compose build identity-svc

docker compose up -d

Note: (if the identity service doesn't start in the mac because port 5000 is already in use uncheck Airplay Receiver in system preferences and try again)

http://localhost:5000/

bob Pass123$

see lesson 78 - Debugging a .Netservice in a docker container - we had an error with the following file:

src/IdentityService/Pages/Diagnostics/Index.cshtml.cs

after fixing bug rebuild the image and start the container

docker compose build identity-svc

docker compose up -d


docker-compose.yaml:

gateway-svc: image: deepakbhari/gateway-svc:latest build: context: . dockerfile: src/GatewayService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Docker - ASPNETCORE_URLS=http://+:80 ports: - 6001:80


This line: - ASPNETCORE_ENVIRONMENT=Docker

will read from the appsettings.Docker.json file instead of appsettings.Development.json

for this service....


docker compose build gateway-svc

docker compose up -d


TO CLEAN UP DATA / RESET APPLICATION TO A KNOWN STATE - things out of sync and redo seed do:

docker compose down docker compose up -d


WAS AN ISSUE HERE:

we had to update the following two files:

src/IdentityService/HostingExtensions.cs

docker-compose.yaml

see lesson 80 - testing docker containers....

after fix do:

docker compose build identity-svc

docker compose up -d

8 Adding the client side application

https://nextjs.org/

cd web-app

npm run dev

code -> setttings -> settings -> search for unknown

CSS › Lint: Unknown At Rules Unknown at-rule.

set to ignore.....


By default anything inside the app folder is considered a server component and rendered on the server side - and return just the html to the client browser

unless we add the 'use client' statement at the top of the component file.

flexboxfroggy.com


Icons:

❯ cd frontend/web-app

npm install react-icons

https://react-icons.github.io/react-icons/


ES7+ React/Redux/React-Native snippets

use rfc shortcut...


For Logging:

In version Next.js 14.0.3 you have to change next.config.js to: /*_ @type {import('next').NextConfig} _/ const nextConfig = {     logging: {         fetches: {           fullUrl: true,         },       }, }   module.exports = nextConfig


aspect ratio does not always work on browsers - so we will install a tailwindcss plugin which is better:

npm install -D @tailwindcss/aspect-ratio

--

corePlugins: { aspectRatio: false, // disable core aspect ratio - and only use the plugin below... }, plugins: [require("@tailwindcss/aspect-ratio")],

--

replace aspect-video (which did nothing for us) with:


_ IMPORTANT STUFF _

npm install react-countdown

https://www.npmjs.com/package/react-countdown

https://www.npmjs.com/package/react-countdown#custom-renderer-with-completed-condition

--

Nextjs will give the following weird error:

Server Error Error: Super expression must either be null or a function This error happened while generating the page. Any console logs will be displayed in the terminal window.

This is because our React Countdown component is using some client side code (javascript) that our browser needs to do something with, however it is currently being rendered on the server....

We need to convert it to a client side component

add the 'use client' directive at the top of the component to convert it


_ SUPPRESS HYDRATION WARNING _

Error due to small difference between client and server code when using React Countdown:

Warning: Text content did not match. Server: "27" Client: "26" at span at div at Countdown$1

** We dont need to worry about this error...

As the data is coming down to our client and our client component is being hydrated, or our server component is being hydrated with the client side code.

Sometime we will see this error / sometimes not....

So we want to suppress this warning.....

we can do this with:

suppressHydrationWarning={true}

    <span suppressHydrationWarning={true}>
      {zeroPad(days)}:{zeroPad(hours)}:{zeroPad(minutes)}:{zeroPad(seconds)}
    </span>

If we want to use React hooks like useState etc - we need to use client side code... We can create smaller client side components which make use of the hooks and add them inside the server components

Like the CarImage component inside of the AuctionCard component....


TypeScript

https://transform.tools/json-to-typescript

use to this to convert an object to a type...

--

Add the type at the highest place where the data is coming in... in this case our getData function:

async function getData(): Promise<PagedResult> { //this is an extended version of fetch - also gives us caching //this is server side fetching - client won't be aware of this const res = await fetch("http://localhost:6001/search?pageSize=10");

if (!res.ok) throw new Error("Failed to fetch data");

return res.json(); }


Pagination

https://www.flowbite-react.com/docs/components/pagination

https://www.flowbite-react.com/docs/getting-started/quickstart

npm i flowbite-react

Note: flowbite components are all client-side components....

so nextjs will complain if you try to use them in a server-side component.

Workaround - put them in a smaller client-side component and then use that inside the server-side component


STATE MANAGEMENT

Zustand

https://docs.pmnd.rs/zustand/getting-started/introduction

npm install zustand query-string

9 Client side identity with NextAuth (AuthJS)

docker compose build identity-svc

docker compose build search-svc


https://next-auth.js.org/getting-started/example

npm install next-auth

https://next-auth.js.org/configuration/initialization

https://next-auth.js.org/getting-started/typescript#module-augmentation


Protecting routes:

https://next-auth.js.org/tutorials/securing-pages-and-api-routes


https://next-auth.js.org/tutorials/securing-pages-and-api-routes#using-gettoken

10 Client app crud actions

npm install react-hook-form react-datepicker

npm install -D @types/react-datepicker


https://react-hook-form.com/get-started

To create reusable inputs see: https://react-hook-form.com/docs/usecontroller


npm install react-hot-toast

https://react-hot-toast.com/


11 Bidding Service

~/Projects/Narsties

Create and configure the service

dotnet new webapi -o src/BiddingService

dotnet sln add src/BiddingService


Nuget packages to add to Bidding Service:

MongoDB.Entities by Đĵ ΝιΓΞΗΛψΚ Microsoft.AspNetCore.Authentication.JwtBearer by Microsoft MassTransit.RabbitMQ by Chris Patterson


cd src/BiddingService

dotnet build // check to see we can build without any errors dotnet watch // start app to check we can connect to rabbitmq and mongodb without any errors

Create the models

...

Make sure our bidding service has access to the classes inside our Contracts

cd src/BiddingService dotnet add reference ../../src/Contracts


Automapper

Install: AutoMapper.Extensions.Microsoft.DependencyInjection by Jimmy Bogard


GRPC

NuGet install into the AuctionService:

Grpc.AspNetCore by The gRPC Authors


Install vscode-proto3 VSCode extension to help with proto files...


Grpc - Server

add to AuctionsService.csproj file:

dotnet build (so that our grpc code get generated by the proto call buffer)


Grpc - Client

NuGet install into the BiddingService:

Google.Protobuf by Google Inc. Grpc.Tools by The gRPC Authors Grpc.Net.Client by The gRPC Authors


add to BiddingService.csproj file:

dotnet build (so that our grpc code get generated by the proto call buffer)


~/Projects/Narsties

docker compose build

docker compose down

docker compose up -d


11 Signal R Service

~/Projects/Narsties

dotnet new web -o src/NotificationService

dotnet sln add src/NotificationService

cd src/NotificationService

Add a reference to the Contracts

dotnet add reference ../../src/Contracts


NuGet Install:

MassTransit.RabbitMQ by Chris Patterson


Copy masstransit config from another program.cs in another service and copy it into this one...

modify launchsettings.json / appsettings.development.json and program.cs

dotnet build dotnet watch


Update the GatewayService to add the notifications


Create Consumers

They will simply recieve the event and send that event out as a message.


Add and configure CORs in the GatewayService


create Dockefile for NotificationService, copy contents of one from another service, add the NotificationService to it.

Make sure all the Dockerfiles for all services have:

COPY Narsties.sln Narsties.sln COPY src/AuctionService/AuctionService.csproj src/AuctionService/AuctionService.csproj COPY src/SearchService/SearchService.csproj src/SearchService/SearchService.csproj COPY src/GatewayService/GatewayService.csproj src/GatewayService/GatewayService.csproj COPY src/BiddingService/BiddingService.csproj src/BiddingService/BiddingService.csproj COPY src/NotificationService/NotificationService.csproj src/NotificationService/NotificationService.csproj COPY src/Contracts/Contracts.csproj src/Contracts/Contracts.csproj COPY src/IdentityService/IdentityService.csproj src/IdentityService/IdentityService.csproj


update the docker-compose.yaml file and add the config for our new service in here...

notify-svc: image: deepakbhari/notify-svc:latest build: context: . dockerfile: src/NotificationService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:80 - RabbitMq__Host=rabbitmq ports: - 7004:80 depends_on: - rabbitmq


~/Projects/Narsties

docker compose build

docker compose down

docker compose up -d


12 Adding Bids / notifications to the client App

cd frontend/web-app

npm install date-fns

npm install @microsoft/signalr


Once logged out and loggin back in again and session hasnt timed out - if we want to force login add:

{ prompt: "login" }

to:

signIn("id-server", { callbackUrl: "/" }, { prompt: "login" })

In the LoginButton.tsx


Zustand Update

[DEPRECATED] Use createWithEqualityFn instead of create

To fix the above error do the below:

The fix is simply to replace and occurrences of import { create } from "zustand" with import { createWithEqualityFn } from "zustand/traditional" and in the useAuctionStore.ts, useBidStore.ts and useParamsStore.ts, change any call made to the create method to createWithEqualityFn. e.g. export const useAuctionStore = create<State & Actions>((set) => ({ to export const useAuctionStore = createWithEqualityFn<State & Actions>((set) => ({ It's a straight substitution, and console warnings are gone.


14 Publishing app to production (locally)

docker compose build web-app

docker compose up -d


https://www.hostinger.co.uk/tutorials/how-to-edit-hosts-file

sudo nano /etc/hosts

Add this to bottom of hosts file: 127.0.0.1 id.narsties.com

Like below:

Host Database

localhost is used to configure the loopback interface

when the system is booting. Do not change this entry.

127.0.0.1 localhost 255.255.255.255 broadcasthost ::1 localhost

Added by Docker Desktop

To allow the same kube context to work on the host and the container:

127.0.0.1 kubernetes.docker.internal

End of section

127.0.0.1 id.narsties.com

to match the same url (id.narsties.com) in docker-compose.yaml:

web-app: image: deepakbhari/web-app build: context: . dockerfile: frontend/web-app/Dockerfile volumes: - /var/lib/web/data ports: - 3000:3000 environment: - NEXTAUTH_SECRET=somethingreallyreallysecret - NEXTAUTH_URL=http://localhost:3000 - NEXTAUTH_URL_INTERNAL=http://web-app:3000 - API_URL=http://gateway-svc/ - ID_URL=http://id.narsties.com - NEXT_PUBLIC_NOTIFICATION_URL=http://gateway-svc/notifications


ping id.narsties.com

Make sure we get a response back....


extra_hosts:
  - "id.narsties.com:172.20.0.7"

web-app: image: deepakbhari/web-app build: context: . dockerfile: frontend/web-app/Dockerfile volumes: - /var/lib/web/data ports: - 3000:3000 extra_hosts: - "id.narsties.com:172.20.0.7" environment: - NEXTAUTH_SECRET=somethingreallyreallysecret - NEXTAUTH_URL=http://localhost:3000 - NEXTAUTH_URL_INTERNAL=http://web-app:3000 - API_URL=http://gateway-svc/ - ID_URL=http://id.narsties.com - NEXT_PUBLIC_NOTIFICATION_URL=http://gateway-svc/notifications


Then for identity-svc

Change

ports:
  - 5000:80

to

ports:
  - 80:80

identity-svc: image: deepakbhari/identity-svc:latest build: context: . dockerfile: src/IdentityService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Docker - ASPNETCORE_URLS=http://+:80 - ConnectionStrings__DefaultConnection=Server=postgres:5432;User Id=postgres;Password=postgrespw;Database=identity ports: - 80:80 depends_on: - postgres gateway-svc: image: deepakbhari/gateway-svc:latest build: context: . dockerfile: src/GatewayService/Dockerfile environment: - ASPNETCORE_ENVIRONMENT=Docker - ASPNETCORE_URLS=http://+:80 ports: - 6001:80


Now we shpould be able to get to identity server from http://id.narsties.com/ in browser.


❯ docker network ls

NETWORK ID NAME DRIVER SCOPE 58d3891bd610 backend_default bridge local 289ca2735194 bridge bridge local d813033c64f2 host host local f59f0ac3bfdc narsties_default bridge local 72fe355dc0b4 none null local 4bc64bcf9827 server_default bridge local


Create static ip addresses....

First we need to create our own network to do that:

networks: custom: ipam: config: - subnet: 10.5.0.0/16

then add network to bottom of each service...


https://hub.docker.com/r/nginxproxy/nginx-proxy

https://github.com/nginx-proxy/nginx-proxy/tree/main/docs#ssl-support

SSL

https://github.com/FiloSottile/mkcert

mkcert is a simple tool for making locally-trusted development certificates.

❯ brew install mkcert

mkcert -h

❯ mkcert -h Usage of mkcert:

    $ mkcert -install
    Install the local CA in the system trust store.

    $ mkcert example.org
    Generate "example.org.pem" and "example.org-key.pem".

    $ mkcert example.com myapp.dev localhost 127.0.0.1 ::1
    Generate "example.com+4.pem" and "example.com+4-key.pem".

    $ mkcert "*.example.it"
    Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".

    $ mkcert -uninstall
    Uninstall the local CA (but do not delete it).

❯ mkcert -install Created a new local CA 💥 Sudo password: The local CA is now installed in the system trust store! ⚡️

~/Pr/Narsties/

❯ mkdir devcerts ❯ cd devcerts

~/Pr/Narsties/devcerts

mkcert -key-file narsties.com.key -cert-file narsties.com.crt app.n arsties.com api.narsties.com id.narsties.com


Created a new certificate valid for the following names 📜

  • "app.narsties.com"
  • "api.narsties.com"
  • "id.narsties.com"

The certificate is at "narsties.com.crt" and the key at "narsties.com.key" ✅

It will expire on 29 April 2026 🗓


FINALLY!!!!

https://app.narsties.com/

About

Building backend with .NET core

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors