Skip to content

Commit

Permalink
Project sync fixes, auth kept for now to be worked on once rc2 is out.
Browse files Browse the repository at this point in the history
  • Loading branch information
Layoric committed Oct 3, 2023
1 parent 9e0ca30 commit 0b119d5
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 25 deletions.
1 change: 0 additions & 1 deletion Gallery.Unified.Client/App.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@using System.Text.RegularExpressions
@inject NavigationManager NavigationManager


<!DOCTYPE html>
<html lang="en">

Expand Down
2 changes: 1 addition & 1 deletion Gallery.Unified.Client/Pages/AutoQueryGrids/Bookings.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/bookings"
@* @attribute [Authorize(Roles = "Employee")] *@
@attribute [Authorize(Roles = "Employee")]
@inject IJSRuntime JS

<Breadcrumbs class="mb-8">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/bookings-auto"
@* @attribute [Authorize(Roles = "Employee")] *@
@attribute [Authorize(Roles = "Employee")]
@inject IJSRuntime JS

<Breadcrumbs class="mb-8">
Expand Down
2 changes: 1 addition & 1 deletion Gallery.Unified.Client/Pages/AutoQueryGrids/Coupons.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/coupons"
@* @attribute [Authorize(Roles = "Employee")] *@
@attribute [Authorize(Roles = "Employee")]

<Breadcrumbs class="mb-8">
<Breadcrumb href="/grid">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/job-applications/comments"
@* @attribute [Authorize] *@
@attribute [Authorize]

<Breadcrumbs class="mb-8">
<Breadcrumb href="/grid">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/job-applications/events"
@* @attribute [Authorize] *@
@attribute [Authorize]

<Breadcrumbs class="mb-8">
<Breadcrumb href="/grid">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/job-offers"
@* @attribute [Authorize] *@
@attribute [Authorize]

<Breadcrumbs class="mb-8">
<Breadcrumb href="/grid">
Expand Down
2 changes: 1 addition & 1 deletion Gallery.Unified.Client/Pages/AutoQueryGrids/Jobs.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/jobs"
@* @attribute [Authorize] *@
@attribute [Authorize]

<Breadcrumbs class="mb-8">
<Breadcrumb href="/grid">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/grid/phone-screens"
@* @attribute [Authorize] *@
@attribute [Authorize]

<Breadcrumbs class="mb-8">
<Breadcrumb href="/grid">
Expand Down
6 changes: 2 additions & 4 deletions Gallery.Unified.Client/Pages/BookingsCrud/Create.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
@* @attribute [Authorize(Roles="Employee")] *@
@* @inherits AppAuthComponentBase *@
@inherits AppComponentBase

@attribute [Authorize(Roles="Employee")]
@inherits AppAuthComponentBase

<form @onsubmit="submit" @onsubmit:preventDefault>
<CascadingValue Value=@api.Error>
Expand Down
5 changes: 2 additions & 3 deletions Gallery.Unified.Client/Pages/BookingsCrud/Edit.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
@* @attribute [Authorize(Roles="Employee")] *@
@* @inherits AppAuthComponentBase *@
@inherits AppComponentBase
@attribute [Authorize(Roles="Employee")]
@inherits AppAuthComponentBase

<form @onsubmit="submit">
<CascadingValue Value=@api.Error>
Expand Down
5 changes: 2 additions & 3 deletions Gallery.Unified.Client/Pages/BookingsCrud/Index.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@page "/bookings-crud"
@* @attribute [Authorize(Roles="Employee")] *@
@* @inherits AppAuthComponentBase *@
@inherits AppComponentBase
@attribute [Authorize(Roles="Employee")]
@inherits AppAuthComponentBase

<h1 class="mb-4 text-2xl font-semibold text-gray-900 dark:text-gray-100">
Bookings CRUD
Expand Down
7 changes: 3 additions & 4 deletions Gallery.Unified.Client/Pages/CallHelloSecure.razor
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@page "/hello-secure"
@* @attribute [Authorize()] *@
@* @inherits AppAuthComponentBase *@
@inherits AppComponentBase
@attribute [Authorize()]
@inherits AppAuthComponentBase

<h1 class="mb-4 text-2xl font-semibold text-gray-900 dark:text-gray-100">
Call HelloSecure
Expand All @@ -19,7 +18,7 @@
</form>

@if (api.Succeeded) {
@* <div class="mt-4 text-gray-400">@User!.GetDisplayName() says:</div> *@
<div class="mt-4 text-gray-400">@User!.GetDisplayName() says:</div>
<h3 class="text-2xl text-green-600 mt-4">@api.Response!.Result</h3>
}

Expand Down
2 changes: 1 addition & 1 deletion Gallery.Unified.Client/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<div class="bg-white landing relative overflow-hidden">
<main>
<div class="pt-32 sm:pt-32 lg:pt-24 lg:pb-14 lg:overflow-hidden bg-violet-900" style="background-image:url(img/bg-header.jpg);background-size:cover;">
<div class="pt-32 sm:pt-32 lg:pt-24 lg:pb-14 lg:overflow-hidden bg-violet-900" style="background-image:url(/img/bg-header.jpg);background-size:cover;">
<div class="mx-auto max-w-7xl lg:px-4">
<div class="lg:grid lg:grid-cols-2 lg:gap-8">
<div class="mx-auto max-w-md px-4 sm:max-w-2xl sm:px-6 sm:text-center lg:px-0 lg:text-left lg:flex lg:items-center">
Expand Down
160 changes: 160 additions & 0 deletions Gallery.Unified.Client/wwwroot/content/deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
title: Deployment with GitHub Actions
summary: Configuring your GitHub repo for SSH and CDN deployments
date: 2021-11-21
WARN: During development Browser Cache needs to be disabled to refresh .md changes
---

# ServiceStack GitHub Action Deployments

The [release.yml](https://github.com/NetCoreTemplates/blazor-tailwind/blob/main/.github/workflows/release.yml)
in this template enables GitHub Actions CI deployment to a dedicated server with SSH access.

## Overview
`release.yml` is designed to work with a ServiceStack app deploying directly to a single server via SSH. A docker image is built and stored on GitHub's `ghcr.io` docker registry when a GitHub Release is created.

GitHub Actions specified in `release.yml` then copy files remotely via scp and use `docker-compose` to run the app remotely via SSH.

## What's the process of `release.yml`?

![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ghr-vanilla-diagram.png)

## Deployment server setup
To get this working, a server needs to be setup with the following:

- SSH access
- docker
- docker-compose
- ports 443 and 80 for web access of your hosted application

This can be your own server or any cloud hosted server like Digital Ocean, AWS, Azure etc.

When setting up your server, you'll want to use a dedicated SSH key for access to be used by GitHub Actions. GitHub Actions will need the *private* SSH key within a GitHub Secret to authenticate. This can be done via ssh-keygen and copying the public key to the authorized clients on the server.

To let your server handle multiple ServiceStack applications and automate the generation and management of TLS certificates, an additional docker-compose file is provided in this template, `nginx-proxy-compose.yml`. This docker-compose file is ready to run and can be copied to the deployment server.

For example, once copied to remote `~/nginx-proxy-compose.yml`, the following command can be run on the remote server.

```
docker-compose -f ~/nginx-proxy-compose.yml up -d
```

This will run an nginx reverse proxy along with a companion container that will watch for additional containers in the same docker network and attempt to initialize them with valid TLS certificates.

### GitHub Actions secrets

The `release.yml` uses the following secrets.

| Required Secrets | Description |
| -- | -- |
| `DEPLOY_API` | Hostname used to SSH deploy .NET App to, this can either be an IP address or subdomain with A record pointing to the server |
| `DEPLOY_USERNAME` | Username to log in with via SSH e.g, **ubuntu**, **ec2-user**, **root** |
| `DEPLOY_KEY` | SSH private key used to remotely access deploy .NET App |
| `LETSENCRYPT_EMAIL` | Email required for Let's Encrypt automated TLS certificates |

To also enable deploying static assets to a CDN:

| Optional Secrets | Description |
| -- | -- |
| `DEPLOY_CDN` | Hostname where static **/wwwroot** assets should be deployed to |

These secrets can use the [GitHub CLI](https://cli.github.com/manual/gh_secret_set) for ease of creation. Eg, using the GitHub CLI the following can be set.

```bash
gh secret set DEPLOY_API -b"<DEPLOY_API>"
gh secret set DEPLOY_USERNAME -b"<DEPLOY_USERNAME>"
gh secret set DEPLOY_KEY < key.pem # DEPLOY_KEY
gh secret set LETSENCRYPT_EMAIL -b"<LETSENCRYPT_EMAIL>"
gh secret set DEPLOY_CDN -b"<DEPLOY_CDN>"
```

These secrets are used to populate variables within GitHub Actions and other configuration files.

## Client UI Deployment

The Blazor Client application is built and deployed to GitHub Pages during the `release.yml` workflow process by committing
the result of `vite build` to `gh-pages` branch in the repository.

### CI .csproj After Build Tasks

The Host Server `.csproj` includes post build instructions populated by GitHub Actions when publishing **Client** assets to CDN
by first copying the generated `index.html` home page into `404.html` in order to enable full page reloads to use Blazor's App
client routing:

```xml
<PropertyGroup>
<ClientDir>$(MSBuildProjectDirectory)/../MyApp.Client</ClientDir>
<WwwRoot>$(ClientDir)/wwwroot</WwwRoot>
</PropertyGroup>

<!-- Populated in release.yml with GitHub Actions secrets -->
<Target Name="DeployApi" AfterTargets="Build" Condition="$(DEPLOY_API) != ''">
<Exec Command="echo DEPLOY_API=$(DEPLOY_API)" />

<!-- Update Production settings with DEPLOY_API Blazor UI should use -->
<WriteLinesToFile File="$(WwwRoot)/appsettings.Production.json"
Lines="$([System.IO.File]::ReadAllText($(WwwRoot)/appsettings.Production.json).Replace('{DEPLOY_API}',$(DEPLOY_API)))"
Overwrite="true" Encoding="UTF-8" />

<!-- 404.html SPA fallback (supported by GitHub Pages, Cloudflare & Netlify CDNs) -->
<Copy SourceFiles="$(WwwRoot)/index.html"
DestinationFiles="$(WwwRoot)/wwwroot/404.html" />

<!-- define /api proxy routes (supported by Cloudflare or Netlify CDNs) -->
<WriteLinesToFile File="$(WwwRoot)/_redirects"
Lines="$([System.IO.File]::ReadAllText($(WwwRoot)/_redirects).Replace('{DEPLOY_API}',$(DEPLOY_API)))"
Overwrite="true" Encoding="UTF-8" />
</Target>
<Target Name="DeployCdn" AfterTargets="Build" Condition="$(DEPLOY_CDN) != ''">
<Exec Command="echo DEPLOY_CDN=$(DEPLOY_CDN)" />

<!-- Define custom domain name that CDN should use -->
<Exec Condition="$(DEPLOY_CDN) != ''" Command="echo $(DEPLOY_CDN) &gt; $(WwwRoot)/CNAME" />
</Target>
```

Whilst the `_redirects` file is a convention supported by many [popular Jamstack CDNs](https://jamstack.wtf/#deployment)
that sets up a new rule that proxies `/api*` requests to where the production .NET App is deployed to in order
for API requests to not need CORS:

```
/api/* {DEPLOY_API}/api/:splat 200
```

By default this template doesn't use the `/api` proxy route & makes CORS API requests so it can be freely hosted
on GitHub pages CDN.

## Pushing updates and rollbacks

By default, deployments of both the **Client** and **Server** occur on commit to your main branch. A new Docker image for your
ServiceStack API is produced, pushed to GHCR.io and hosted on your Linux server with Docker Compose.
Your Blazor WASM UI is built and pushed to the repository GitHub Pages.

The template also will run the release process on the creation of a GitHub Release making it easier to switch to manual production releases.

Additionally, the `release.yml` workflow can be run manually specifying a version. This enables production rollbacks based on previously tagged releases.
A release must have already been created for the rollback build to work, it doesn't create a new Docker build based on previous code state, only redeploys as existing Docker image.

## No CORS Hosting Options

The `CorsFeature` needs to be enabled when adopting our recommended deployment configuration of having static
`/wwwroot` assets hosted from a CDN in order to make cross-domain requests to your .NET APIs.

### Using a CDN Proxy
Should you want to, our recommended approach to avoid your App making CORS requests is to define an `/api` proxy route
on your CDN to your `$DEPLOY_API` server.

To better support this use-case, this template includes populating the `_redirects` file used by popular CDNs like
[Cloudflare proxy redirects](https://developers.cloudflare.com/pages/platform/redirects) and
[Netlify proxies](https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service) to define
redirect and proxy rules. For AWS CloudFront you would need to define a
[Behavior for a custom origin](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html).

### No CDN

Of course the easiest solution is to not need CORS in the first place by not deploying to a CDN and serving both **Server**
and Blazor Client **UI** from your .NET App. This would be the preferred approach when deploying within an Intranet where
network speeds are much faster in order for initial load times to be heavily reduced.

However when deploying to a public site on the Internet we'd highly recommend deploying Blazor WASM's static assets to a CDN
so load times can be reduced as much as possible.
81 changes: 81 additions & 0 deletions Gallery.Unified.Client/wwwroot/content/hosting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: Hosting Costs
WARN: During development Browser Cache needs to be disabled to refresh .md changes
---

# App Hosting Costs

<a href="https://jamstack.org">
<img src="/img/jamstack-icon.svg" style="width:3.5rem;height:3.5rem;float:left;margin:.5rem 1rem 0 0">
</a>

The modern [jamstack.org](https://jamstack.org) approach for developing websites is primarily concerned with adopting
the architecture yielding the best performance and superior UX by minimizing the time to first byte from serving
pre-built static assets from CDN edge caches.

## Cheaper Hosting

<a href="https://jamstack.org">
<img src="/img/emoji-money.svg" style="width:3.5rem;height:3.5rem;float:left;margin:.5rem 1rem 0 0">
</a>

A consequence of designing your UI decoupled from your back-end server is that it also becomes considerably
cheaper to host as its static files can be hosted by any web server and is a task highly optimized by CDNs
who are able to provide generous free & low cost hosting options.

## [/MyApp.Client](https://github.com/NetCoreTemplates/blazor-tailwind/tree/main/MyApp.Client)

This template takes advantage of its decoupled architecture and uses [GitHub Actions to deploy](/docs/deploy)
a copy of its static UI generated assets and hosted on:

### GitHub Pages CDN

### [blazor-wasm.jamstacks.net](https://blazor-wasm.jamstacks.net)

This is an optional deployment step which publishes a copy of your .NET App's `/wwwroot` folder to this templates
[gh-pages](https://github.com/NetCoreTemplates/blazor-tailwind/tree/gh-pages) branch where it's automatically served from
[GitHub Pages CDN](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages) at **no cost**.

It's an optional but recommended optimization as it allows the initial download from your website to be served
directly from CDN edge caches.

## [/MyApp](https://github.com/NetCoreTemplates/blazor-tailwind/tree/main/MyApp)

The .NET 6 `/MyApp` backend server is required for this App's dynamic functions including the Hello API on the home page
and its [built-in Authentication](https://docs.servicestack.net/auth).

The C# project still contains the complete App and can be hosted independently with the entire App served
directly from its deployed ASP.NET Core server at:

### Digital Ocean

### [blazor-wasm-api.jamstacks.net](https://blazor-wasm-api.jamstacks.net)

But when accessed from the CDN [blazor-wasm.jamstacks.net](https://blazor-wasm.jamstacks.net) that contains a
copy of its static `/wwwroot` UI assets, only its back-end JSON APIs are used to power its dynamic features.

## Total Cost

<a href="https://www.digitalocean.com/pricing">
<img src="/img/digital-ocean.svg" style="width:6.5rem;height:6.5rem;float:left;margin:0 1rem 0 0">
</a>

Since hosting on GitHub Pages CDN is free, the only cost is for hosting this App's .NET Server which is being hosted
from a basic [$10 /mo](https://www.digitalocean.com/pricing) droplet which is currently hosting **25** .NET Docker
Apps and demos of [starting project templates](https://servicestack.net/start) which works out to be just under **$0.40 /mo**!

## Jamstack Benefits

Jamstack is quickly becoming the preferred architecture for the development of modern web apps with
[benefits](https://jamstack.org/why-jamstack/) that extend beyond performance to improved:

- **Security** from a reduced attack surface from hosting read-only static resources and requiring fewer App Servers
- **Scale** with non-essential load removed from App Servers to CDN's architecture capable of incredible scale & load capacity
- **Maintainability** resulting from reduced hosting complexity and the clean decoupling of UI and server logic
- **Portability** with your static UI assets being easily capable from being deployed and generically hosted from any CDN or web server
- **Developer Experience** with the major JavaScript frameworks at the forefront of amazing DX are embracing Jamstack in their dev model, libraries & tooling

Best of all the Jamstack approach fits perfectly with ServiceStack's recommended
[API First Development](https://docs.servicestack.net/api-first-development) model which encourages development of
reusable message-based APIs where the same System APIs can be reused from all Web, Mobile & Desktop Apps
from multiple HTTP, MQ or gRPC endpoints.
2 changes: 1 addition & 1 deletion Gallery.Unified.Client/wwwroot/css/app.css

Large diffs are not rendered by default.

0 comments on commit 0b119d5

Please sign in to comment.