Skip to content

Commit 979bad6

Browse files
committed
Use KAMAL_DEPLOY_HOST instead of $DEPLOY_API
1 parent b92e998 commit 979bad6

File tree

5 files changed

+163
-69
lines changed

5 files changed

+163
-69
lines changed

MyApp.Client/_posts/ai-chat.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: 'AI Chat - A Customizable, Private, ChatGPT-like UI'
3+
excerpt: 'Configuring your GitHub repo for SSH and CDN deployments'
4+
coverImage: '/assets/blog/dynamic-routing/cover.jpg'
5+
date: '2021-11-09T00:00:00.000Z'
6+
author:
7+
name: Author
8+
picture: '/assets/blog/authors/author1.svg'
9+
ogImage:
10+
url: '/assets/blog/dynamic-routing/cover.jpg'
11+
---
12+
13+
# ServiceStack GitHub Action Deployments
14+
15+
The [release.yml](https://github.com/NetCoreTemplates/nextjs/blob/main/.github/workflows/release.yml)
16+
in this template enables GitHub Actions CI deployment to a dedicated server with SSH access.
17+
18+
## Overview
19+
`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.
20+
21+
GitHub Actions specified in `release.yml` then copy files remotely via scp and use `docker-compose` to run the app remotely via SSH.
22+
23+
## What's the process of `release.yml`?
24+
25+
![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ghr-vanilla-diagram.png)
26+
27+
## Deployment server setup
28+
To get this working, a server needs to be setup with the following:
29+
30+
- SSH access
31+
- docker
32+
- docker-compose
33+
- ports 443 and 80 for web access of your hosted application
34+
35+
This can be your own server or any cloud hosted server like Digital Ocean, AWS, Azure etc.
36+
37+
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.
38+
39+
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.
40+
41+
For example, once copied to remote `~/nginx-proxy-compose.yml`, the following command can be run on the remote server.
42+
43+
```
44+
docker-compose -f ~/nginx-proxy-compose.yml up -d
45+
```
46+
47+
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.
48+
49+
### GitHub Actions secrets
50+
51+
The `release.yml` uses the following secrets.
52+
53+
| Required Secrets | Description |
54+
| -- | -- |
55+
| `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 |
56+
| `DEPLOY_USERNAME` | Username to log in with via SSH e.g, **ubuntu**, **ec2-user**, **root** |
57+
| `DEPLOY_KEY` | SSH private key used to remotely access deploy .NET App |
58+
| `LETSENCRYPT_EMAIL` | Email required for Let's Encrypt automated TLS certificates |
59+
60+
To also enable deploying static assets to a CDN:
61+
62+
| Optional Secrets | Description |
63+
| -- | -- |
64+
| `DEPLOY_CDN` | Hostname where static **/wwwroot** assets should be deployed to |
65+
66+
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.
67+
68+
```bash
69+
gh secret set DEPLOY_API -b"<DEPLOY_API>"
70+
gh secret set DEPLOY_USERNAME -b"<DEPLOY_USERNAME>"
71+
gh secret set DEPLOY_KEY < key.pem # DEPLOY_KEY
72+
gh secret set LETSENCRYPT_EMAIL -b"<LETSENCRYPT_EMAIL>"
73+
gh secret set DEPLOY_CDN -b"<DEPLOY_CDN>"
74+
```
75+
76+
These secrets are used to populate variables within GitHub Actions and other configuration files.
77+
78+
## UI Deployment
79+
80+
The Next.js `ui` application is built and deployed to GitHub Pages during the `release.yml` workflow process by committing the result of `npm run build` to `gh-pages` branch in the repository.
81+
82+
Variable replacement of `$DEPLOY_API` and `$DEPLOY_CDN` is performed on the following files as a way to coordinate configuration between the `ui` and `api` project.
83+
84+
- `ui/next.config.js` - Set backend .NET API URL for UI App to use
85+
- `ui/post.build.js` - If exists, run from GitHub Action after `npm run build`
86+
87+
### post.build.js
88+
89+
The `post.build.js` script helps when also publishing `/ui` assets to CDN by first copying the generated
90+
`index.html` home page into `404.html` in order to enable full page reloads to use SPA client routing:
91+
92+
```js
93+
const fs = require("fs")
94+
const path = require("path")
95+
96+
// Replaced in release.yml with GitHub Actions secrets
97+
const DEPLOY_API = 'https://$DEPLOY_API'
98+
const DEPLOY_CDN = 'https://$DEPLOY_CDN'
99+
100+
const DIST = '../api/Jamstacks/wwwroot'
101+
102+
// 404.html SPA fallback (supported by GitHub Pages, Cloudflare & Netlify CDNs)
103+
fs.copyFileSync(
104+
path.resolve(`${DIST}/index.html`),
105+
path.resolve(`${DIST}/404.html`))
106+
107+
// Define Virtual Host for GitHub Pages CDN
108+
fs.writeFileSync(`${DIST}/CNAME`, DEPLOY_CDN)
109+
110+
// Define /api proxy routes (supported by Cloudflare or Netlify CDNs)
111+
fs.writeFileSync(`${DIST}/_redirects`,
112+
fs.readFileSync(`${DIST}/_redirects`, 'utf-8')
113+
.replace(/{DEPLOY_API}/g, DEPLOY_API))
114+
```
115+
116+
Whilst the `_redirects` file is a convention supported by many [popular Jamstack CDNs](https://jamstack.wtf/#deployment)
117+
that sets up a new rule that proxies `/api*` requests to where the production .NET App is deployed to in order
118+
for API requests to not need CORS:
119+
120+
```
121+
/api/* {DEPLOY_API}/api/:splat 200
122+
```
123+
124+
By default this template doesn't use the `/api` proxy route & makes CORS API requests so it can be freely hosted
125+
on GitHub pages CDN.
126+
127+
## Pushing updates and rollbacks
128+
129+
By default, deployments of both the `ui` and `api` 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.
130+
Your React UI is built and pushed to the repository GitHub Pages.
131+
132+
The template also will run the release process on the creation of a GitHub Release making it easier to switch to manual production releases.
133+
134+
Additionally, the `release.yml` workflow can be run manually specifying a version. This enables production rollbacks based on previously tagged releases.
135+
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.
136+
137+
## No CORS Hosting Options
138+
139+
The `CorsFeature` needs to be enabled when adopting our recommended deployment configuration of having static
140+
`/wwwroot` assets hosted from a CDN in order to make cross-domain requests to your .NET APIs.
141+
142+
### Using a CDN Proxy
143+
Should you want to, our recommended approach to avoid your App making CORS requests is to define an `/api` proxy route
144+
on your CDN to your `$DEPLOY_API` server.
145+
146+
To better support this use-case, this template includes populating the `_redirects` file used by popular CDNs like
147+
[Cloudflare proxy redirects](https://developers.cloudflare.com/pages/platform/redirects) and
148+
[Netlify proxies](https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service) to define
149+
redirect and proxy rules. For AWS CloudFront you would need to define a
150+
[Behavior for a custom origin](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html).
151+
152+
### No CDN
153+
154+
Of course the easiest solution is to not need CORS in the first place by not deploying to a CDN and serving both `/api`
155+
and UI from your .NET App. But this would forgo all the performance & UX benefits that has made
156+
[Jamstack](https://jamstack.org) approach so popular.

MyApp.Client/_posts/rider.md

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -85,35 +85,3 @@ excellent multi-terminal support to run `$ dotnet watch` and `$ npm run dev` fro
8585

8686
![](https://github.com/ServiceStack/docs/raw/master/docs/images/spa/vue-vite-rider-terminals.png)
8787

88-
### Deploying to Production
89-
90-
When you're ready to deploy your App you can create a production build with:
91-
92-
```bash
93-
$ npm run publish
94-
```
95-
96-
Which will generate production builds of your C# projects and npm projects with its static generated UI assets
97-
written to `/wwwroot` to be deployed together with your complete .NET App.
98-
99-
Our recommendation for the best possible responsive UX is to deploy your App's `/wwwwroot` static assets to a CDN in
100-
order for the initial load of your App to be downloaded from nearby CDN edge caches.
101-
102-
To do this configure the production url the UI should use for all its `/api` Ajax requests by modifying
103-
`DEPLOY_API` in your `next.config.ts`:
104-
105-
```csharp
106-
const DEPLOY_API = 'https://$DEPLOY_API'
107-
```
108-
109-
This template also includes the necessary GitHub Actions to deploy this Apps production static assets to GitHub Pages CDN,
110-
for more info, checkout [GitHub Actions Deployments](/posts/deploy).
111-
112-
### Get Started
113-
114-
Driven by the static typing benefits of TypeScript, React bundles full
115-
[Type Definitions with their libraries](https://reactjs.org/docs/static-type-checking.html#type-definitions)
116-
that development IDEs like Rider take full advantage of that's used to power its type-safe & productive intelli-sense dev UX.
117-
118-
If you're new to React a good place to start is
119-
[React Getting Started](https://reactjs.org/docs/getting-started.html).

MyApp.Client/_posts/vs.md

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -65,35 +65,3 @@ much improved intelli-sense, navigation, tests runner & debug capabilities.
6565
As we've never had a satisfactory experience trying develop npm SPA projects with VS.NET, we'd recommend only
6666
running the C# `/api` project in VS.NET and continuing to use VSCode for `/ui` project.
6767

68-
### Deploying to Production
69-
70-
When you're ready to deploy your App you can create a production build with:
71-
72-
```bash
73-
$ npm run publish
74-
```
75-
76-
Which will generate production builds of your C# projects and npm projects with its static generated UI assets
77-
written to `/wwwroot` to be deployed together with your complete .NET App.
78-
79-
Our recommendation for the best possible responsive UX is to deploy your App's `/wwwwroot` static assets to a CDN in
80-
order for the initial load of your App to be downloaded from nearby CDN edge caches.
81-
82-
To do this configure the production url the UI should use for all its `/api` Ajax requests by modifying
83-
`DEPLOY_API` in your `next.config.ts`:
84-
85-
```csharp
86-
const DEPLOY_API = 'https://$DEPLOY_API'
87-
```
88-
89-
This template also includes the necessary GitHub Actions to deploy this Apps production static assets to GitHub Pages CDN,
90-
for more info, checkout [GitHub Actions Deployments](/posts/deploy).
91-
92-
### Get Started
93-
94-
Driven by the static typing benefits of TypeScript, React bundles full
95-
[Type Definitions with their libraries](https://reactjs.org/docs/static-type-checking.html#type-definitions)
96-
that development IDEs like Rider take full advantage of that's used to power its type-safe & productive intelli-sense dev UX.
97-
98-
If you're new to React a good place to start is
99-
[React Getting Started](https://reactjs.org/docs/getting-started.html).

MyApp.Client/next-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// <reference types="next" />
22
/// <reference types="next/image-types/global" />
3-
import "./.next/types/routes.d.ts";
3+
import "./dist/dev/types/routes.d.ts";
44

55
// NOTE: This file should not be edited
66
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

MyApp.Client/next.config.mjs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import createMDX from "@next/mdx"
22

3-
// TODO: replace with production URL of .NET App
4-
const DEPLOY_API = 'https://$DEPLOY_API' // e.g. 'https://nextjs-api.jamstacks.net'
5-
63
const target = process.env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${process.env.ASPNETCORE_HTTPS_PORT}` :
74
process.env.ASPNETCORE_URLS ? process.env.ASPNETCORE_URLS.split(';')[0] : 'https://localhost:5001';
85

96
const isProd = process.env.NODE_ENV === 'production'
107
const buildLocal = process.env.MODE === 'local'
118
const API_URL = isProd ? DEPLOY_API : (buildLocal ? '' : target)
129

13-
console.log('next.config.mjs', process.env.NODE_ENV, buildLocal, API_URL)
10+
// TODO: replace with production URL of .NET App, e.g. https://nextjs.web-templates.io
11+
const DEPLOY_API = process.env.KAMAL_DEPLOY_HOST
12+
? `https://${process.env.KAMAL_DEPLOY_HOST}`
13+
: target
14+
15+
console.log('next.config.mjs', process.env.NODE_ENV, buildLocal, API_URL, process.env.KAMAL_DEPLOY_HOST)
1416

1517
/**
1618
* @type {import('next').NextConfig}

0 commit comments

Comments
 (0)