Skip to content

Add Next.js setup with Docker support to publishers playground #8690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions playground/publishers/Publishers.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@

builder.AddDockerfile("mycontainer", "qots");

builder.AddAdminPortal();

#if !SKIP_DASHBOARD_REFERENCE
// This project is only added in playground projects to support development/debugging
// of the dashboard. It is not required in end developer code. Comment out this code
Expand All @@ -60,3 +62,19 @@
#endif

builder.Build().Run();

internal static class AdminPortalExtensions
{
public static IResourceBuilder<NodeAppResource> AddAdminPortal(this IDistributedApplicationBuilder builder)
{
var app = builder.AddNpmApp("adminportal", "../adminportal", "dev")
.WithHttpEndpoint(env: "PORT")
.PublishAsDockerFile();

var npmInstall = builder.AddExecutable($"{app.Resource.Name}-npm-install", "npm", "../adminportal", "install")
.WithParentRelationship(app);

app.WaitForCompletion(npmInstall);
return app;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<AspireProjectOrPackageReference Include="Aspire.Hosting.Azure.PostgreSQL" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Kubernetes" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.Docker" />
<AspireProjectOrPackageReference Include="Aspire.Hosting.NodeJs" />
<ProjectReference Include="..\Publishers.Frontend\Publishers.Frontend.csproj" />

<ProjectReference Include="..\Publishers.ApiService\Publishers.ApiService.csproj" />
Expand Down
17 changes: 13 additions & 4 deletions playground/publishers/Publishers.DbSetup/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@
using var app = builder.Build();
using var scope = app.Services.CreateScope();
using var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();

var created = await db.Database.EnsureCreatedAsync();
if (created)
while (true)
Copy link
Preview

Copilot AI Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an infinite loop for database creation retry may lead to an indefinite hang if the database setup continuously fails. Consider introducing a maximum retry count or an exponential backoff mechanism to prevent potential issues in production.

Suggested change
while (true)
const int maxRetryCount = 5;
int retryCount = 0;
int delay = 1000; // initial delay in milliseconds
while (retryCount < maxRetryCount)

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use waitfor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't work in deployed scenarios.

{
Console.WriteLine("Database created!");
}
try
{
await db.Database.EnsureCreatedAsync();
break;
}
catch (Exception ex)
{
logger.LogError(ex, "Error creating database.");
await Task.Delay(1000);
}
}
1 change: 1 addition & 0 deletions playground/publishers/adminportal/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
37 changes: 37 additions & 0 deletions playground/publishers/adminportal/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Use the official Node.js 18 image as the base image
FROM node:18-alpine AS builder

# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package.json package-lock.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Build the Next.js app
RUN npm run build

# Use a minimal image for production
FROM node:18-alpine AS runner

# Set the working directory
WORKDIR /app

# Copy the built app from the builder stage
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Install only production dependencies
RUN npm install --production

# Expose the port the app runs on
EXPOSE 3000

# Start the Next.js app
CMD ["npm", "start"]
36 changes: 36 additions & 0 deletions playground/publishers/adminportal/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

Check failure on line 1 in playground/publishers/adminportal/README.md

View workflow job for this annotation

GitHub Actions / lint

First line in a file should be a top-level heading [Context: "This is a [Next.js](https://ne..."]

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
Binary file added playground/publishers/adminportal/app/favicon.ico
Binary file not shown.
26 changes: 26 additions & 0 deletions playground/publishers/adminportal/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@import "tailwindcss";

:root {
--background: #ffffff;
--foreground: #171717;
}

@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}

body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
}
34 changes: 34 additions & 0 deletions playground/publishers/adminportal/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
103 changes: 103 additions & 0 deletions playground/publishers/adminportal/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Image from "next/image";

export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2 tracking-[-.01em]">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
app/page.tsx
</code>
.
</li>
<li className="tracking-[-.01em]">
Save and see your changes instantly.
</li>
</ol>

<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org →
</a>
</footer>
</div>
);
}
7 changes: 7 additions & 0 deletions playground/publishers/adminportal/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
/* config options here */
};

export default nextConfig;
Loading
Loading