From 79c2b62306026ba7ddcfb9a88c2851d4e54fe986 Mon Sep 17 00:00:00 2001
From: Richard Grover <38992201+richgrov@users.noreply.github.com>
Date: Tue, 20 May 2025 08:44:52 -0600
Subject: [PATCH] Initial commit
---
.dockerignore | 3 +
Dockerfile | 58 +++++++
app/page.js | 148 +++++++----------
app/page.module.css | 214 ++++++++++++------------
package-lock.json | 388 ++++++++++++++++++++++++++++++++++++++++++++
package.json | 5 +-
6 files changed, 617 insertions(+), 199 deletions(-)
create mode 100644 .dockerignore
create mode 100644 Dockerfile
create mode 100644 package-lock.json
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..7651bc4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+.env
+.next/
+node_modules/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7316ce6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,58 @@
+# syntax=docker.io/docker/dockerfile:1
+
+FROM node:18-alpine AS base
+
+# Install dependencies only when needed
+FROM base AS deps
+# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+RUN npm i -g bun
+
+# Install dependencies based on the preferred package manager
+COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
+RUN npm i --frozen-lockfile
+
+
+# Rebuild the source code only when needed
+FROM base AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+
+# Next.js collects completely anonymous telemetry data about general usage.
+# Learn more here: https://nextjs.org/telemetry
+# Uncomment the following line in case you want to disable telemetry during the build.
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN npm run build
+
+# Production image, copy all the files and run next
+FROM base AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+# Uncomment the following line in case you want to disable telemetry during runtime.
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+COPY --from=builder /app/public ./public
+
+# Automatically leverage output traces to reduce image size
+# https://nextjs.org/docs/advanced-features/output-file-tracing
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+USER nextjs
+
+EXPOSE 3000
+
+ENV PORT=3000
+
+# server.js is created by next build from the standalone output
+# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
+ENV HOSTNAME="0.0.0.0"
+CMD ["node", "server.js"]
diff --git a/app/page.js b/app/page.js
index a9c3e29..232d47d 100644
--- a/app/page.js
+++ b/app/page.js
@@ -1,95 +1,67 @@
-import Image from "next/image";
import styles from "./page.module.css";
+import postgres from "postgres";
+
+const sql = postgres({
+ host: "localhost:5432",
+ username: process.env["USERNAME"],
+ password: process.env["PASSWORD"],
+});
+
+await sql`CREATE TABLE IF NOT EXISTS Messages(name text, message text);`;
+
+function Form() {
+ async function onSubmit(formData) {
+ "use server";
+ const name = formData.get("name");
+ const message = formData.get("message");
+ await sql`INSERT INTO Messages(name, message) VALUES (${name}, ${message})`;
+ router.refresh();
+ }
+
+ return (
+
+ );
+}
+
+function Message(props) {
+ const user = props.user;
+ const text = props.text;
+ return (
+
+ {user}: {text}
+
+ );
+}
+
+export default async function Home() {
+ const messages = await sql`SELECT * FROM Messages`;
+
+ const messageComponents = [];
+ let i = -1;
+ for (const message of messages) {
+ i++;
+ messageComponents.push(
+ ,
+ );
+ }
-export default function Home() {
return (
-
-
-
- -
- Get started by editing
app/page.js.
-
- - Save and see your changes instantly.
-
-
-
-
-
+
+ {messageComponents}
);
}
diff --git a/app/page.module.css b/app/page.module.css
index a11c8f3..24a7f11 100644
--- a/app/page.module.css
+++ b/app/page.module.css
@@ -1,168 +1,164 @@
.page {
- --gray-rgb: 0, 0, 0;
- --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
- --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
+ --gray-rgb: 0, 0, 0;
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
- --button-primary-hover: #383838;
- --button-secondary-hover: #f2f2f2;
+ --button-primary-hover: #383838;
+ --button-secondary-hover: #f2f2f2;
- display: grid;
- grid-template-rows: 20px 1fr 20px;
- align-items: center;
- justify-items: center;
- min-height: 100svh;
- padding: 80px;
- gap: 64px;
- font-family: var(--font-geist-sans);
+ min-height: 100svh;
+ padding: 80px;
+ gap: 64px;
+ font-family: var(--font-geist-sans);
}
@media (prefers-color-scheme: dark) {
- .page {
- --gray-rgb: 255, 255, 255;
- --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
- --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
+ .page {
+ --gray-rgb: 255, 255, 255;
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
- --button-primary-hover: #ccc;
- --button-secondary-hover: #1a1a1a;
- }
+ --button-primary-hover: #ccc;
+ --button-secondary-hover: #1a1a1a;
+ }
}
.main {
- display: flex;
- flex-direction: column;
- gap: 32px;
- grid-row-start: 2;
+ display: flex;
+ flex-direction: column;
+ gap: 32px;
+ grid-row-start: 2;
}
.main ol {
- font-family: var(--font-geist-mono);
- padding-left: 0;
- margin: 0;
- font-size: 14px;
- line-height: 24px;
- letter-spacing: -0.01em;
- list-style-position: inside;
+ font-family: var(--font-geist-mono);
+ padding-left: 0;
+ margin: 0;
+ font-size: 14px;
+ line-height: 24px;
+ letter-spacing: -0.01em;
+ list-style-position: inside;
}
.main li:not(:last-of-type) {
- margin-bottom: 8px;
+ margin-bottom: 8px;
}
.main code {
- font-family: inherit;
- background: var(--gray-alpha-100);
- padding: 2px 4px;
- border-radius: 4px;
- font-weight: 600;
+ font-family: inherit;
+ background: var(--gray-alpha-100);
+ padding: 2px 4px;
+ border-radius: 4px;
+ font-weight: 600;
}
.ctas {
- display: flex;
- gap: 16px;
+ display: flex;
+ gap: 16px;
}
.ctas a {
- appearance: none;
- border-radius: 128px;
- height: 48px;
- padding: 0 20px;
- border: none;
- border: 1px solid transparent;
- transition:
- background 0.2s,
- color 0.2s,
- border-color 0.2s;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- line-height: 20px;
- font-weight: 500;
+ appearance: none;
+ border-radius: 128px;
+ height: 48px;
+ padding: 0 20px;
+ border: none;
+ border: 1px solid transparent;
+ transition:
+ background 0.2s,
+ color 0.2s,
+ border-color 0.2s;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ line-height: 20px;
+ font-weight: 500;
}
a.primary {
- background: var(--foreground);
- color: var(--background);
- gap: 8px;
+ background: var(--foreground);
+ color: var(--background);
+ gap: 8px;
}
a.secondary {
- border-color: var(--gray-alpha-200);
- min-width: 158px;
+ border-color: var(--gray-alpha-200);
+ min-width: 158px;
}
.footer {
- grid-row-start: 3;
- display: flex;
- gap: 24px;
+ grid-row-start: 3;
+ display: flex;
+ gap: 24px;
}
.footer a {
- display: flex;
- align-items: center;
- gap: 8px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
}
.footer img {
- flex-shrink: 0;
+ flex-shrink: 0;
}
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
- a.primary:hover {
- background: var(--button-primary-hover);
- border-color: transparent;
- }
+ a.primary:hover {
+ background: var(--button-primary-hover);
+ border-color: transparent;
+ }
- a.secondary:hover {
- background: var(--button-secondary-hover);
- border-color: transparent;
- }
+ a.secondary:hover {
+ background: var(--button-secondary-hover);
+ border-color: transparent;
+ }
- .footer a:hover {
- text-decoration: underline;
- text-underline-offset: 4px;
- }
+ .footer a:hover {
+ text-decoration: underline;
+ text-underline-offset: 4px;
+ }
}
@media (max-width: 600px) {
- .page {
- padding: 32px;
- padding-bottom: 80px;
- }
+ .page {
+ padding: 32px;
+ padding-bottom: 80px;
+ }
- .main {
- align-items: center;
- }
+ .main {
+ align-items: center;
+ }
- .main ol {
- text-align: center;
- }
+ .main ol {
+ text-align: center;
+ }
- .ctas {
- flex-direction: column;
- }
+ .ctas {
+ flex-direction: column;
+ }
- .ctas a {
- font-size: 14px;
- height: 40px;
- padding: 0 16px;
- }
+ .ctas a {
+ font-size: 14px;
+ height: 40px;
+ padding: 0 16px;
+ }
- a.secondary {
- min-width: auto;
- }
+ a.secondary {
+ min-width: auto;
+ }
- .footer {
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- }
+ .footer {
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: center;
+ }
}
@media (prefers-color-scheme: dark) {
- .logo {
- filter: invert();
- }
+ .logo {
+ filter: invert();
+ }
}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..3587683
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,388 @@
+{
+ "name": "chat-were-back",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "chat-were-back",
+ "version": "0.1.0",
+ "dependencies": {
+ "next": "15.3.2",
+ "postgres": "^3.4.5",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.1",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.1.0",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.3.2",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.3.2",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001718",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "license": "MIT"
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.3.2",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.3.2",
+ "@swc/counter": "0.1.3",
+ "@swc/helpers": "0.5.15",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.3.2",
+ "@next/swc-darwin-x64": "15.3.2",
+ "@next/swc-linux-arm64-gnu": "15.3.2",
+ "@next/swc-linux-arm64-musl": "15.3.2",
+ "@next/swc-linux-x64-gnu": "15.3.2",
+ "@next/swc-linux-x64-musl": "15.3.2",
+ "@next/swc-win32-arm64-msvc": "15.3.2",
+ "@next/swc-win32-x64-msvc": "15.3.2",
+ "sharp": "^0.34.1"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postgres": {
+ "version": "3.4.5",
+ "license": "Unlicense",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/porsager"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.1",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.3",
+ "semver": "^7.7.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.1",
+ "@img/sharp-darwin-x64": "0.34.1",
+ "@img/sharp-libvips-darwin-arm64": "1.1.0",
+ "@img/sharp-libvips-darwin-x64": "1.1.0",
+ "@img/sharp-libvips-linux-arm": "1.1.0",
+ "@img/sharp-libvips-linux-arm64": "1.1.0",
+ "@img/sharp-libvips-linux-ppc64": "1.1.0",
+ "@img/sharp-libvips-linux-s390x": "1.1.0",
+ "@img/sharp-libvips-linux-x64": "1.1.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.1.0",
+ "@img/sharp-linux-arm": "0.34.1",
+ "@img/sharp-linux-arm64": "0.34.1",
+ "@img/sharp-linux-s390x": "0.34.1",
+ "@img/sharp-linux-x64": "0.34.1",
+ "@img/sharp-linuxmusl-arm64": "0.34.1",
+ "@img/sharp-linuxmusl-x64": "0.34.1",
+ "@img/sharp-wasm32": "0.34.1",
+ "@img/sharp-win32-ia32": "0.34.1",
+ "@img/sharp-win32-x64": "0.34.1"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "license": "0BSD"
+ }
+ }
+}
diff --git a/package.json b/package.json
index 249fbda..cf2103a 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,9 @@
"lint": "next lint"
},
"dependencies": {
+ "next": "15.3.2",
+ "postgres": "^3.4.5",
"react": "^19.0.0",
- "react-dom": "^19.0.0",
- "next": "15.3.2"
+ "react-dom": "^19.0.0"
}
}