diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 23d3929..b7b46ad 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -9,13 +9,9 @@ } }, "customizations": { - "codespaces": { - "openFiles": [ - "codespaces.md" - ] - }, "vscode": { "extensions": [ + "angular.ng-template", "mongodb.mongodb-vscode" ] } diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..77e71ef --- /dev/null +++ b/.mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "MongoDB": { + "command": "npx", + "args": [ + "-y", + "mongodb-mcp-server@latest", + "--connectionString", + "mongodb://localhost:27017/meanStackExample" + ] + } + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d34252e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template", "mongodb.mongodb-vscode"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..2e58d01 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "pwa-chrome", + "request": "launch", + "preLaunchTask": "npm: start - client", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test - client", + "url": "http://localhost:9876/debug.html" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..95b5ce0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "mdb.presetConnections": [ + { + "name": "Local Database", + "connectionString": "mongodb://localhost:27017" + } + ], + "mdb.showOverviewPageAfterInstall": false, + "workbench.secondarySideBar.defaultVisibility": "hidden" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..1b88c46 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,46 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "path": "client", + "label": "npm: start - client", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "path": "client", + "label": "npm: test - client", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ] +} diff --git a/client/src/app/api-config.service.ts b/client/src/app/api-config.service.ts index 9381b23..e1f4976 100644 --- a/client/src/app/api-config.service.ts +++ b/client/src/app/api-config.service.ts @@ -1,11 +1,15 @@ import { Service, PLATFORM_ID, inject } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; +import { resolveApiBaseUrl } from './api-url'; @Service() export class ApiConfigService { - // In browser (including Codespaces), target current host on API port. - // During SSR/build contexts, fall back to localhost for deterministic behavior. - readonly baseUrl = isPlatformBrowser(inject(PLATFORM_ID)) - ? `${window.location.protocol}//${window.location.hostname}:5300` + private readonly platformId = inject(PLATFORM_ID); + + // In the browser, derive the API URL from the current location so it works + // across local dev and hosted/forwarded environments. During SSR/build, + // fall back to localhost for deterministic behavior. + readonly baseUrl = isPlatformBrowser(this.platformId) + ? resolveApiBaseUrl(window.location) : 'http://localhost:5300'; } diff --git a/client/src/app/api-url.ts b/client/src/app/api-url.ts new file mode 100644 index 0000000..a46f035 --- /dev/null +++ b/client/src/app/api-url.ts @@ -0,0 +1,32 @@ +const API_PORT = 5300; + +/** + * Resolves the API base URL for a given browser location. + * + * In most environments the API is reached on the same host at `API_PORT`. + * Hosting platforms that forward each port as its own subdomain are handled + * transparently, so callers never need to know about them. + */ +export function resolveApiBaseUrl(location: { + protocol: string; + hostname: string; +}): string { + const { protocol, hostname } = location; + const forwardedHost = forwardedApiHost(hostname); + + return forwardedHost + ? `${protocol}//${forwardedHost}` + : `${protocol}//${hostname}:${API_PORT}`; +} + +/** + * GitHub Codespaces forwards each port as its own subdomain + * (e.g. `name-4200.app.github.dev`) served over 443 rather than as + * `host:port`. Rewrite the client's port segment to the API port. + * Returns `null` for hosts that don't use this scheme. + */ +function forwardedApiHost(hostname: string): string | null { + const match = /^(.*)-\d+\.app\.github\.dev$/.exec(hostname); + + return match ? `${match[1]}-${API_PORT}.app.github.dev` : null; +} diff --git a/codespaces.md b/codespaces.md deleted file mode 100644 index 3cff11a..0000000 --- a/codespaces.md +++ /dev/null @@ -1,78 +0,0 @@ -# ๐Ÿ‘‹ Welcome to the MEAN Stack Example - -This Codespace is **already up and running** โ€” dependencies are installed, the database is seeded, and both the API and the Angular client have started for you. There's nothing to `npm install` or `npm start`; just wait a few seconds for the servers to finish booting. - -## ๐Ÿš€ What's running - -| Service | Port | Description | -|---------|------|-------------| -| **Angular Client** | `4200` | The web app (opens automatically in a Simple Browser preview) | -| **Express API** | `5300` | REST API for employee CRUD operations | -| **MongoDB** | `27017` | Local database, pre-seeded with sample employees | - -The **Angular Client** preview opens on its own when the client finishes compiling. If you don't see it (or you closed it), open the **Ports** tab, find port **4200** ("Angular Client"), and click the ๐ŸŒ globe icon to open the preview. - -> โณ First launch takes a moment while the client compiles. If the preview shows an error at first, give it a few seconds and refresh. - -## ๐Ÿงญ Try it out - -In the client preview you can: - -- **View** the seeded list of employees. -- **Add a New Employee** with the button below the list. -- **Edit** or **Delete** any row with the action buttons. - -Changes are saved through the Express API to MongoDB and reflected in the list immediately. - -You can also hit the API directly from the terminal: - -```bash -curl http://localhost:5300/healthcheck # โ†’ {"status":"ok"} -curl http://localhost:5300/employees # โ†’ the seeded employees -``` - -## ๐Ÿ—‚๏ธ Project structure - -``` -server/ Express + MongoDB REST API (TypeScript) - src/ - server.ts App entry, CORS, /healthcheck, mounts the router - employee.routes.ts CRUD routes for /employees - database.ts MongoDB connection + schema validation -client/ Angular 22 app (standalone, zoneless, SSR) - src/app/ - employees-list/ List view (Material table) - employee-form/ Shared reactive form - add-employee/ Create page - edit-employee/ Edit page (loaded via a route resolver) - employee.service.ts httpResource + CRUD calls to the API -``` - -## โœ๏ธ Making changes - -Both servers run in **watch mode** โ€” just edit and save: - -- Edit files under **`client/src/`** โ†’ the client rebuilds and the preview reloads. -- Edit files under **`server/src/`** โ†’ restart the API from the terminal if needed: - ```bash - cd server && npm start - ``` - -## ๐ŸŒฑ Reseeding the database - -The database is seeded on startup. To reset it to the sample data at any time: - -```bash -npm run seed -``` - -## ๐Ÿงช Running tests - -```bash -npm test # client + server unit tests -npm run test:integration # API integration tests (in-memory MongoDB) -``` - -## ๐Ÿ“š Learn more - -This project is the companion code for the [MEAN Stack Tutorial](https://www.mongodb.com/resources/languages/mean-stack-tutorial). Happy hacking! ๐ŸŽ‰