Skip to content

Commit 4dece66

Browse files
Migrate Web Frontend to Official Aspire JavaScript Hosting (#7731)
* chore(deps): remove Node.js community plugin * refactor(AppHost): integrate web frontend with Aspire JS hosting * docs(AppHost): update web frontend and Aspire documentation * feat(AppHost): configure Azurite emulator with persistent data volume * docs(AppHost): fix ClientsPath description to reference worktrees section * fix(Apphost): run dotnet format
1 parent 970cacd commit 4dece66

4 files changed

Lines changed: 39 additions & 34 deletions

File tree

AppHost/AppHost.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
builder.ConfigureIdp();
1414
var services = builder.ConfigureServices(db, secretsSetup, mail, azurite);
1515

16-
#if ENABLE_NODEJS_COMMUNITY_PLUGIN
1716
builder.ConfigureWebFrontend(services["api"]);
18-
#endif
1917

2018
#if ENABLE_NGROK_COMMUNITY_PLUGIN
2119
builder.ConfigureNgrok((services["billing"], "http"));

AppHost/AppHost.csproj

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
<!-- Disable community plugins by default -->
1212
<EnableNgrokCommunityPlugin>false</EnableNgrokCommunityPlugin>
1313
<DefineConstants Condition="'$(EnableNgrokCommunityPlugin)' == 'true'">$(DefineConstants);ENABLE_NGROK_COMMUNITY_PLUGIN</DefineConstants>
14-
<EnableNodeJsCommunityPlugin>false</EnableNodeJsCommunityPlugin>
15-
<DefineConstants Condition="'$(EnableNodeJsCommunityPlugin)' == 'true'">$(DefineConstants);ENABLE_NODEJS_COMMUNITY_PLUGIN</DefineConstants>
1614
</PropertyGroup>
1715

1816
<ItemGroup>
@@ -26,10 +24,6 @@
2624
<PackageReference Include="CommunityToolkit.Aspire.Hosting.Ngrok" Version="[13.3.0]" />
2725
</ItemGroup>
2826

29-
<ItemGroup Condition="'$(EnableNodeJsCommunityPlugin)' == 'true'">
30-
<PackageReference Include="CommunityToolkit.Aspire.Hosting.NodeJS.Extensions" Version="[9.9.0]"/>
31-
</ItemGroup>
32-
3327
<ItemGroup>
3428
<ProjectReference Include="..\src\Admin\Admin.csproj"/>
3529
<ProjectReference Include="..\src\Api\Api.csproj"/>

AppHost/BuilderExtensions.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Aspire.Hosting.Azure;
2+
using Aspire.Hosting.JavaScript;
23
using Azure.Provisioning;
34
using Azure.Provisioning.Storage;
45

@@ -66,11 +67,14 @@ public static IResourceBuilder<AzureStorageResource> ConfigureAzurite(this IDist
6667
MaxAgeInSeconds = new BicepValue<int>("30")
6768
}));
6869
})
69-
.RunAsEmulator(c =>
70+
.RunAsEmulator(emulator =>
7071
{
71-
c.WithBlobPort(10000)
72+
emulator
73+
.WithBlobPort(10000)
7274
.WithQueuePort(10001)
73-
.WithTablePort(10002);
75+
.WithTablePort(10002)
76+
.WithDataVolume()
77+
.WithLifetime(ContainerLifetime.Persistent);
7478
});
7579

7680
builder
@@ -256,30 +260,29 @@ private static (string Name, string Tag) GetImageParts(this IDistributedApplicat
256260
private static bool IsSelfHosted(this IDistributedApplicationBuilder builder) =>
257261
builder.Configuration["SelfHost"]?.Equals("true", StringComparison.OrdinalIgnoreCase) == true;
258262

259-
#if ENABLE_NODEJS_COMMUNITY_PLUGIN
260263
public static void ConfigureWebFrontend(this IDistributedApplicationBuilder builder,
261264
IResourceBuilder<ProjectResource> api)
262265
{
263266
if (!int.TryParse(builder.Required("WebFrontend:Port"), out var port))
264267
throw new InvalidOperationException("Invalid value for WebFrontend:Port.");
265268

266269
builder
267-
.AddBitwardenNpmApp("web-frontend", "web", api)
268-
.WithHttpsEndpoint(port, port, "angular-http", isProxied: false)
270+
.AddBitwardenNpmApp("web-frontend", "web", api, port: port)
269271
.WithUrl(builder.Required("WebFrontend:Url"))
270272
.WithExternalHttpEndpoints();
271273
}
272274

273-
private static IResourceBuilder<NodeAppResource> AddBitwardenNpmApp(this IDistributedApplicationBuilder builder,
274-
string name, string path, IResourceBuilder<ProjectResource> api, string scriptName = "build:bit:watch")
275+
private static IResourceBuilder<JavaScriptAppResource> AddBitwardenNpmApp(this IDistributedApplicationBuilder builder,
276+
string name, string path, IResourceBuilder<ProjectResource> api, int port, string scriptName = "build:bit:watch")
275277
{
276278
return builder
277-
.AddNpmApp(name, $"{builder.Required("ClientsPath")}/{path}", scriptName)
279+
.AddJavaScriptApp(name, $"{builder.Required("ClientsPath")}/{path}", scriptName)
280+
.WithHttpsEndpoint(port, port, "angular-http", isProxied: false)
281+
.WithNpm(install: false)
278282
.WithReference(api)
279283
.WaitFor(api)
280284
.WithExplicitStart();
281285
}
282-
#endif
283286

284287
#if ENABLE_NGROK_COMMUNITY_PLUGIN
285288
public static void ConfigureNgrok(this IDistributedApplicationBuilder builder,

AppHost/README.md

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ the services wait for the database and secrets setup to finish before launching.
3030
| `setup-secrets` | Executable | Runs `dev/setup_secrets.ps1` — applies `dev/secrets.json` to all projects |
3131
| `mssql` | SQL Server 2022 container | Persistent data volume, port 1433 |
3232
| `run-db-migrations` | Executable | Runs `dev/migrate.ps1` against `vault_dev` (or `self_host_dev`) |
33-
| `azurite` | Azure Storage emulator | Blob :10000 · Queue :10001 · Table :10002 |
33+
| `azurite` | Azure Storage emulator | Blob :10000 · Queue :10001 · Table :10002 · persistent data volume |
3434
| `azurite-setup` | Executable | Runs `dev/setup_azurite.ps1` after Azurite is ready |
3535
| `mailcatcher` | Container | SMTP :10250 · Web UI :1080 |
3636
| `redis` | Container | Redis with AOF persistence, port 6379 |
@@ -71,7 +71,7 @@ dotnet user-secrets set "Database:Password" "<your-sa-password>"
7171
| Key | Default | Description |
7272
|-----------------------------|------------------------------------|--------------------------------------------------------------------------------------|
7373
| `SelfHost` | `false` | Switch to self-hosted mode (see [Self-Hosted Mode](#self-hosted-mode)) |
74-
| `ClientsPath` | `../../clients/apps` | Path to the `clients` repo's `apps/` directory (used by the Node.js plugin) |
74+
| `ClientsPath` | `../../clients/apps` | Path to the `clients` repo's `apps/` directory (see [Git Worktrees](#git-worktrees)) |
7575
| `WorkingDirectory` | `../dev` | Directory where dev scripts are resolved |
7676
| `Services:<name>:BasePort` | see `appsettings.Development.json` | HTTP port for each service; pre-filled to match each service's `launchSettings.json` |
7777
| `Database:Image` | `mssql/server:2022-latest` | Docker image for SQL Server |
@@ -85,33 +85,23 @@ dotnet user-secrets set "Database:Password" "<your-sa-password>"
8585
| `MailCatcher:SmtpPort` | `10250` | Host SMTP port |
8686
| `MailCatcher:WebPort` | `1080` | MailCatcher web UI port |
8787
| `NgrokAuthToken` | _(empty)_ | ngrok auth token (used only when ngrok plugin is enabled) |
88-
| `WebFrontend:Port` | `8080` | Web frontend port (used only when Node.js plugin is enabled) |
88+
| `WebFrontend:Port` | `8080` | Web frontend port |
8989
| `WebFrontend:Url` | `https://bitwarden.test:8080` | Web frontend URL shown in the dashboard |
9090

9191
## Optional Features
9292

93-
### Web Frontend (Node.js community plugin)
93+
### Web Frontend
9494

9595
Runs the web client alongside the server services. Requires the Bitwarden
9696
[clients](https://github.com/bitwarden/clients) repo cloned as a sibling to `server`.
9797

98-
1. Create an `AppHost.csproj.user` file next to `AppHost.csproj` (it is covered by `.gitignore`):
99-
100-
```xml
101-
<Project>
102-
<PropertyGroup>
103-
<EnableNodeJsCommunityPlugin>true</EnableNodeJsCommunityPlugin>
104-
</PropertyGroup>
105-
</Project>
106-
```
107-
108-
2. If the clients repo is not at `../../clients/apps`, override the path:
98+
1. If the clients repo is not at `../../clients/apps`, override the path:
10999

110100
```bash
111101
dotnet user-secrets set "ClientsPath" "<path/to/clients/apps>"
112102
```
113103

114-
3. Run `dotnet run` as normal. The `web-frontend` resource starts with **explicit start** — open
104+
2. Run `dotnet run` as normal. The `web-frontend` resource starts with **explicit start** — open
115105
the Aspire dashboard and start it manually when you're ready.
116106

117107
### Ngrok (Billing Webhook Tunneling)
@@ -194,6 +184,26 @@ variables for every resource.
194184
- If you create an `appsettings.local.json`, add it to `.gitignore` before writing any values
195185
to it.
196186

187+
## Git Worktrees
188+
189+
Path-based settings resolve relative to where you run `dotnet run`. If your worktree lives in
190+
a different location than the main checkout, those paths won't resolve correctly. Use absolute
191+
paths for any such setting:
192+
193+
- **`ClientsPath`** defaults to `../../clients/apps` — override if your worktree is not
194+
alongside the `clients` repo:
195+
196+
```bash
197+
dotnet user-secrets set "ClientsPath" "<absolute/path/to/clients/apps>"
198+
```
199+
200+
- **`AdditionalProjects:<name>:Path`** — use an absolute path when adding projects via user
201+
secrets in a worktree:
202+
203+
```bash
204+
dotnet user-secrets set "AdditionalProjects:<name>:Path" "<absolute/path/to/Project.csproj>"
205+
```
206+
197207
## Troubleshooting
198208

199209
| Symptom | Fix |

0 commit comments

Comments
 (0)