diff --git a/README.md b/README.md index 7291047..692c866 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ Have you ever attended a wedding or a birthday party? If yes, you probably saw photo booth there. You get in, take a photo and paste it in the guest book - simple and fun. But what if we could bring this fun into the digital world? This is where **LensUp** comes to the rescue. **LensUp** is a web application that serves as a virtual gallery, allowing party guests to upload their photos from the event and also write down their wishes. -- [Project status](#Project status) -- [100 days roadmap](#100 days roadmap) -- [How to run LensUp locally](#How to run LensUp locally) -- [TODO list (100 days)](#TODO list (100 days)) +- [Project status](#project-status) +- [100 days roadmap](#100-days-roadmap) +- [How to run LensUp locally](#how-to-run-lensup-locally) +- [TODO list (100 days)](#todo-list-(100-days)) # Project status The video shows the project status as of `26.04.2024` and the core functionality of the application. @@ -73,26 +73,85 @@ Description: ## How to run LensUp locally -You can run the project locally on your machine using Docker. All services will be hosted on your WLAN network. Follow the steps below to run the application locally. +You can run the project locally on your machine using Docker. Follow the steps below to run the application locally: -1. Before we start you should generate `dev-certs` for LensUp on your machine. This operation is required to hosting ASP.NET Core images with Docker over HTTPS. So generate a certificate and configure the local machine: +1. Before we start you should generate `dev-certs` for **LensUp** on your machine. This operation is required to hosting ASP.NET Core images with Docker over HTTPS. So generate a certificate using these commands: ```bash dotnet dev-certs https -ep "%USERPROFILE%\.aspnet\https\lens-up.pfx" -p localCertPassword dotnet dev-certs https --trust ``` - **Replace `%USERPROFILE%` with your computer name.** Example `"C:\Users\Kamil\.aspnet\https\lens-up.pfx"` + **Replace `%USERPROFILE%` with your computer name.** Example `"C:\Users\Dell Precision 7520\.aspnet\https\lens-up.pfx"` - **This is necessary step, because docker-compose refers to that certificate**. + **For local development purposes, we will use the password `localCertPassword`. Do not change this, as the same password is used in the `docker-compose.yml` file.** -2. Install `docker desktop` on your machine (skip if you already done it). + The above commands should generate a `lens-up.pfx` certificate, and should place it in the directory as shown in the screenshot below. + + ![lens-up-cert](/docs/lens-up-cert.png) + + **This is necessary step, because docker-compose refers to that certificate!** + +2. Install `docker desktop` on your machine *(skip if you already done it)*. 3. Run your `docker desktop` application. -4. To be continued. +4. In the main project directory (`lens-up`), where the `docker-compose.yml` file is located, run the command `docker-compose build`. This will build 7 necessary LensUp images. After completing these steps, you should see new images in the Docker Desktop application. + + ![lens-up-docker-images](/docs/lens-up-docker-images.png) + +5. After the build command, run the `docker-compose up` to start the entire infrastructure. You should see in Docker Desktop that 7 containers related to LensUp have been started. + + ![lens-up-containers](/docs/lens-up-containers.png) + +6. Now the entire application is running on your machine. You can use the following addresses: + + - Backend services: + + - `LensUp.BackOfficeService.API` swagger - https://localhost:8085/swagger/index.html + - `LensUp.GalleryService.API` swagger - https://localhost:8083/swagger/index.html + - `LensUp.GalleryService.WebhookTriggerSimulator` - http://localhost:8086/ + - `LensUp.PhotoCollectorService.API` swagger - https://localhost:8081/swagger/index.html + + - UI applications: + + - `LensUp.GalleryService.UI` - http://localhost:5001/ + + - `LensUp.PhotoCollectorService.UI` - http://localhost:5002/ + + *On LensUp.PhotoCollectorService.UI you will see error page, because you need to navigate to the view associated with a specific gallery, which you haven't created yet.* + + + +**How to create your first gallery and have fun with LensUp?** + +1. Go to `LensUp.BackOfficeService.API` - https://localhost:8085/swagger/index.html + +2. Use `Create` endpoint to create your gallery. The endpoint returns the gallery identifier after it is created **(1)**. + + ![lens-up-create-endpoint](/docs/lens-up-create-endpoint.png) + +3. Before using the gallery, we need to activate it. In that case use `Activate` endpoint and pass `galleryId` and `endDate` in request body. Remember the `endDate` is validated and must be greater than the current time. Otherwise, your gallery will be treated as expired. The endpoint returns the gallery `enterCode` after it is activated **(1)**. + + ![lens-up-activate-endpoint](/docs/lens-up-activate-endpoint.png) + +4. With your gallery `enterCode` you can open your gallery using `LensUp.GalleryService.UI` - http://localhost:5001/ + + Log in to your gallery using `enterCode`. + + ![lens-up-login-form](/docs/lens-up-login-form.png) + +5. Now you can scan gallery QR code and upload photos to it. The code redirects to a form for adding photos to the gallery. You can use browser tool to scan QR code or if it doesn't work you can just go to `http://localhost:5002/upload-photo/{enterCode}`. + + ![lens-up-gallery-qr-code](/docs/lens-up-gallery-qr-code.png) + +6. QR Code redirects you to add photo and wishes form. Now you can upload your data to gallery. + + ![lens-up-gallery-qr-code](/docs/lens-up-upload-photo-form.png) +7. After successfully completing the form, we should see success notification and the photo should appear in the gallery. + ![lens-up-upload-flow](/docs/lens-up-upload-flow.gif) ## TODO list (100 days) diff --git a/backend-services/Directory.Packages.props b/backend-services/Directory.Packages.props index 6df5888..96faae3 100644 --- a/backend-services/Directory.Packages.props +++ b/backend-services/Directory.Packages.props @@ -4,7 +4,6 @@ true - @@ -24,9 +23,5 @@ - - - - \ No newline at end of file diff --git a/backend-services/back-office-service/src/LensUp.BackOfficeService.API/appsettings.Development.json b/backend-services/back-office-service/src/LensUp.BackOfficeService.API/appsettings.Development.json index 8cb9e1f..1d5811c 100644 --- a/backend-services/back-office-service/src/LensUp.BackOfficeService.API/appsettings.Development.json +++ b/backend-services/back-office-service/src/LensUp.BackOfficeService.API/appsettings.Development.json @@ -10,6 +10,6 @@ "AzureStorage": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://host.docker.internal:10000/devstoreaccount1;QueueEndpoint=http://host.docker.internal:10001/devstoreaccount1;TableEndpoint=http://host.docker.internal:10002/devstoreaccount1;" }, "ApplicationOptions": { - "PhotoCollectorUIUrl": "http://localhost:5002" + "PhotoCollectorUIUrl": "http://localhost:5002/upload-photo" } } diff --git a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/AppSettingsKeys.cs b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/AppSettingsKeys.cs new file mode 100644 index 0000000..f0c6e8c --- /dev/null +++ b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/AppSettingsKeys.cs @@ -0,0 +1,7 @@ +namespace LensUp.GalleryService.WebhookTriggerSimulator; + +internal static class AppSettingsKeys +{ + public const string AzureWebJobsAzureStorageConnectionString = "AzureWebJobsAzureStorageConnectionString"; + public const string WebhookUrl = "WebhookUrl"; +} diff --git a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/Dockerfile b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/Dockerfile index 40921b8..7517c92 100644 --- a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/Dockerfile +++ b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/Dockerfile @@ -1,26 +1,26 @@ +FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0 AS base +WORKDIR /home/site/wwwroot +EXPOSE 8080 -FROM mcr.microsoft.com/dotnet/sdk:8.0 AS installer-env - +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src COPY ["Directory.Build.props", "/"] -COPY ["Directory.Packages.props", "/"] COPY ["StyleCop.ruleset", "/"] COPY ["photo-collector-service/src/LensUp.PhotoCollectorService.Contracts/LensUp.PhotoCollectorService.Contracts.csproj", "/photo-collector-service/src/LensUp.PhotoCollectorService.Contracts/"] COPY ["common/src/LensUp.Common.Types/LensUp.Common.Types.csproj", "/common/src/LensUp.Common.Types/"] COPY ["gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj", "/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/"] - -RUN dotnet restore "gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj" - -COPY . /src/func-app - -RUN cd /src/func-app && \ -mkdir -p /home/site/wwwroot && \ -dotnet publish gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj -c Release --output /home/site/wwwroot - -FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0 +RUN dotnet restore "/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj" +COPY . . +WORKDIR "/src/." +RUN dotnet build "./gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /home/site/wwwroot +COPY --from=publish /app/publish . ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabled=true - -EXPOSE 8080 -EXPOSE 8086 - -COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"] \ No newline at end of file diff --git a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj index d0711d4..51439a7 100644 --- a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj +++ b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/LensUp.GalleryService.WebhookTriggerSimulator.csproj @@ -7,14 +7,17 @@ enable Linux ..\.. + false + false - - - - - + + + + + + diff --git a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/WebhookTriggerSimulatorFunction.cs b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/WebhookTriggerSimulatorFunction.cs index c232e6b..c5470e0 100644 --- a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/WebhookTriggerSimulatorFunction.cs +++ b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/WebhookTriggerSimulatorFunction.cs @@ -2,6 +2,7 @@ using LensUp.Common.Types.Constants; using LensUp.PhotoCollectorService.Contracts.Events; using Microsoft.Azure.Functions.Worker; +using Microsoft.Extensions.Configuration; using System.Text; using System.Text.Json; @@ -10,13 +11,13 @@ namespace LensUp.GalleryService.WebhookTriggerSimulator; // Workaround - simulate webhook trigger public sealed class WebhookTriggerSimulatorFunction { - private const string WebhookUrl = "http://localhost:8082/Webhook"; + private readonly string WebhookUrl = Environment.GetEnvironmentVariable(AppSettingsKeys.WebhookUrl) ?? throw new ArgumentNullException(AppSettingsKeys.WebhookUrl); public WebhookTriggerSimulatorFunction() { } [Function(nameof(WebhookTriggerSimulatorFunction))] - public async Task Run([QueueTrigger(QueueNames.PhotoQueue, Connection = "AzureStorageConnectionString")] QueueMessage message) + public async Task Run([QueueTrigger(QueueNames.PhotoQueue, Connection = AppSettingsKeys.AzureWebJobsAzureStorageConnectionString)] QueueMessage message) { if (message?.Body == null) @@ -37,7 +38,7 @@ public async Task Run([QueueTrigger(QueueNames.PhotoQueue, Connection = "AzureSt using var client = new HttpClient(clientHandler); var content = new StringContent(JsonSerializer.Serialize(@event), Encoding.UTF8, "application/json"); - await client.PostAsync(WebhookUrl, content); + await client.PostAsync(this.WebhookUrl, content); } catch (Exception exception) { diff --git a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/host.json b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/host.json index ee5cf5f..0126a38 100644 --- a/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/host.json +++ b/backend-services/gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/host.json @@ -1,12 +1,17 @@ { - "version": "2.0", - "logging": { - "applicationInsights": { - "samplingSettings": { - "isEnabled": true, - "excludedTypes": "Request" - }, - "enableLiveMetricsFilters": true - } + "version": "2.0", + "extensions": { + "queues": { + "maxPollingInterval": "00:00:02", } + }, + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + } + } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 0e2791b..34b4f21 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,6 +31,22 @@ services: depends_on: - azurite + lensup.photocollectorservice.ui: + build: + context: ./ui-applications + dockerfile: Dockerfile + args: + SERVICE: photo-collector-ui + image: lensup.photocollectorservice.ui + container_name: lensup.photocollectorservice.ui + ports: + - "5002:5002" + environment: + - NODE_ENV=dev + command: npm run dev + depends_on: + - lensup.photocollectorservice.api + lensup.galleryservice.api: environment: - ASPNETCORE_ENVIRONMENT=Development @@ -51,19 +67,37 @@ services: depends_on: - azurite - # lensup.galleryservice.webhooktriggersimulator: - # environment: - # - AzureFunctionsJobHost__Http__Port=8086 - # container_name: lensup.galleryservice.webhooktriggersimulator - # image: lensup.galleryservice.webhooktriggersimulator - # build: - # context: ./backend-services - # dockerfile: gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/Dockerfile - # ports: - # - "8086:8080" - # depends_on: - # - azurite - # - lensup.galleryservice.api + lensup.galleryservice.webhooktriggersimulator: + environment: + - AzureFunctionsJobHost__Http__Port=8086 + - AzureWebJobsAzureStorageConnectionString=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;QueueEndpoint=http://host.docker.internal:10001/devstoreaccount1; + - WebhookUrl=http://host.docker.internal:8082/Webhook + container_name: lensup.galleryservice.webhooktriggersimulator + image: lensup.galleryservice.webhooktriggersimulator + build: + context: ./backend-services + dockerfile: gallery-service/src/LensUp.GalleryService.WebhookTriggerSimulator/Dockerfile + ports: + - "8086:8080" + depends_on: + - azurite + - lensup.galleryservice.api + + lensup.galleryservice.ui: + build: + context: ./ui-applications + dockerfile: Dockerfile + args: + SERVICE: gallery-ui + image: lensup.galleryservice.ui + container_name: lensup.galleryservice.ui + ports: + - "5001:5001" + environment: + - NODE_ENV=dev + command: npm run dev + depends_on: + - lensup.galleryservice.api lensup.backofficeservice.api: environment: diff --git a/docs/lens-up-activate-endpoint.png b/docs/lens-up-activate-endpoint.png new file mode 100644 index 0000000..750f618 Binary files /dev/null and b/docs/lens-up-activate-endpoint.png differ diff --git a/docs/lens-up-cert.png b/docs/lens-up-cert.png new file mode 100644 index 0000000..5888488 Binary files /dev/null and b/docs/lens-up-cert.png differ diff --git a/docs/lens-up-containers.png b/docs/lens-up-containers.png new file mode 100644 index 0000000..ba2590a Binary files /dev/null and b/docs/lens-up-containers.png differ diff --git a/docs/lens-up-create-endpoint.png b/docs/lens-up-create-endpoint.png new file mode 100644 index 0000000..c94620e Binary files /dev/null and b/docs/lens-up-create-endpoint.png differ diff --git a/docs/lens-up-docker-images.png b/docs/lens-up-docker-images.png new file mode 100644 index 0000000..fae9378 Binary files /dev/null and b/docs/lens-up-docker-images.png differ diff --git a/docs/lens-up-gallery-qr-code.png b/docs/lens-up-gallery-qr-code.png new file mode 100644 index 0000000..665eae2 Binary files /dev/null and b/docs/lens-up-gallery-qr-code.png differ diff --git a/docs/lens-up-login-form.png b/docs/lens-up-login-form.png new file mode 100644 index 0000000..4c4eec9 Binary files /dev/null and b/docs/lens-up-login-form.png differ diff --git a/docs/lens-up-upload-flow.gif b/docs/lens-up-upload-flow.gif new file mode 100644 index 0000000..b0ec0fc Binary files /dev/null and b/docs/lens-up-upload-flow.gif differ diff --git a/docs/lens-up-upload-photo-form.png b/docs/lens-up-upload-photo-form.png new file mode 100644 index 0000000..37c571a Binary files /dev/null and b/docs/lens-up-upload-photo-form.png differ diff --git a/ui-applications/Dockerfile b/ui-applications/Dockerfile new file mode 100644 index 0000000..f093676 --- /dev/null +++ b/ui-applications/Dockerfile @@ -0,0 +1,13 @@ +FROM node:alpine as builder +ARG SERVICE +WORKDIR /usr/src/app + +COPY --chown=node:node package*.json lerna.json tsconfig.json ./ +COPY --chown=node:node packages/shared-components ./packages/shared-components +COPY --chown=node:node packages/${SERVICE} ./packages/${SERVICE} +RUN npm install --loglevel notice --unsafe-perm + +ENV SERVICE_NAME=${SERVICE} + +EXPOSE 5000 +CMD ["npm", "--prefix", "services/${SERVICE}", "start"] \ No newline at end of file diff --git a/ui-applications/packages/back-office-ui/Dockerfile b/ui-applications/packages/back-office-ui/Dockerfile deleted file mode 100644 index 5f7ff80..0000000 --- a/ui-applications/packages/back-office-ui/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:alpine as build - -WORKDIR /app - -COPY package*.json ./ - -RUN npm install - -COPY . . - -EXPOSE 5000 - -CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/ui-applications/packages/gallery-ui/Dockerfile b/ui-applications/packages/gallery-ui/Dockerfile deleted file mode 100644 index 8f08ff2..0000000 --- a/ui-applications/packages/gallery-ui/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:alpine as build - -WORKDIR /app - -COPY package*.json ./ - -RUN npm install - -COPY . . - -EXPOSE 5001 - -CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/ui-applications/packages/gallery-ui/package.json b/ui-applications/packages/gallery-ui/package.json index 4435945..a2a99ec 100644 --- a/ui-applications/packages/gallery-ui/package.json +++ b/ui-applications/packages/gallery-ui/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@lens-up/shared-components": "file:../shared-components", + "@microsoft/signalr": "^8.0.0", "@reduxjs/toolkit": "^2.2.3", "framer-motion": "^11.1.7", "localforage": "^1.10.0", diff --git a/ui-applications/packages/gallery-ui/src/pages/Home/components/QRCodeCard.tsx b/ui-applications/packages/gallery-ui/src/pages/Home/components/QRCodeCard.tsx index 9504c52..e3dcea5 100644 --- a/ui-applications/packages/gallery-ui/src/pages/Home/components/QRCodeCard.tsx +++ b/ui-applications/packages/gallery-ui/src/pages/Home/components/QRCodeCard.tsx @@ -1,3 +1,5 @@ +import { memo } from "react" + interface IQRCodeCardProps { qRCodeUrl: string hasPhotos?: boolean @@ -14,4 +16,4 @@ const QRCodeCard = ({ qRCodeUrl, hasPhotos }: IQRCodeCardProps) => { ) } -export default QRCodeCard \ No newline at end of file +export default memo(QRCodeCard) \ No newline at end of file diff --git a/ui-applications/packages/photo-collector-ui/Dockerfile b/ui-applications/packages/photo-collector-ui/Dockerfile deleted file mode 100644 index cf9a8bb..0000000 --- a/ui-applications/packages/photo-collector-ui/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM node:alpine as build - -WORKDIR /app - -COPY package*.json ./ - -RUN npm install - -COPY . . - -EXPOSE 5002 - -CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/ui-applications/packages/shared-components/src/images/lens-up-logo.png b/ui-applications/packages/shared-components/src/images/lens-up-logo.png index 5f95c58..4943998 100644 Binary files a/ui-applications/packages/shared-components/src/images/lens-up-logo.png and b/ui-applications/packages/shared-components/src/images/lens-up-logo.png differ