Skip to content
Merged
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
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Designed for use on iPads, laptops, or other devices commonly used by students.

## Setup

To run this project locally, you will need to have Node.js installed. The application consists of a backend API and a frontend client, which can be run together with a single command.
To run this project locally, you will need to have Node.js installed.

1. Clone the repository:

Expand All @@ -55,28 +55,38 @@ cd DigitalBonesBox
```

2. Install dependencies:
This command will install the necessary packages for both the root project and the boneset-api server.

```bash
npm install
npm install --prefix boneset-api
```

3. Run the application:
3. Run the application (recommended — one origin):

To start both the backend API server and the frontend server concurrently, with the server automatically reloaded each time changes are made, run
Express serves both the API and the frontend. Open [http://127.0.0.1:8000/](http://127.0.0.1:8000/).

```bash
npm run dev
npm start --prefix boneset-api
```

Or, to run without live server reloading, run
Alternatively, for API-only development with nodemon:

```bash
npm start
npm run dev:api
```

Your browser should automatically open to the application.
The older dual-process scripts (`npm start` / `npm run dev`) still start Express plus `http-server`, but the UI must be opened via the Express origin (`http://127.0.0.1:8000/`) because API calls use same-origin relative URLs.

## Deploy (Vercel)

The app deploys through a single Express entrypoint at `api/index.js` (see `vercel.json`).

- Preview: from a linked Git branch or `npx vercel`
- Production: `npx vercel --prod` or merge to the production branch after the Vercel project is connected

Static UI assets and bone images are copied into `public/` during install so Vercel can serve them on the CDN.

No application environment variables are required for the current build.

## Contributing

Expand Down
6 changes: 6 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);
const { app } = require("../boneset-api/server.js");

export default app;
1 change: 1 addition & 0 deletions boneset-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon"
},
Expand Down
14 changes: 10 additions & 4 deletions boneset-api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ const PORT = process.env.PORT || 8000;
app.use(cors());
app.use(express.json());

const API_BASE_URL = `http://127.0.0.1:${PORT}`;

const LOCAL_DATA_DIR = path.join(__dirname, "data");
const BONESET_DIR = path.join(LOCAL_DATA_DIR, "boneset");
const BONES_DIR = path.join(LOCAL_DATA_DIR, "bones");
Expand All @@ -22,6 +20,7 @@ const TEXT_LABEL_ANNOTATIONS_DIR = path.join(LOCAL_DATA_DIR, "annotations", "tex
const ROTATIONS_TEMPLATE_DIR = path.join(LOCAL_DATA_DIR, "annotations", "rotations annotations");
const DESCRIPTIONS_DIR = path.join(LOCAL_DATA_DIR, "descriptions");
const IMAGES_DIR = path.join(LOCAL_DATA_DIR, "images");
const TEMPLATES_DIR = path.join(__dirname, "../templates");

const BONESET_NAMES = ["bony_pelvis", "skull", "thorax", "vertebrae", "upper_limb", "lower_limb"];

Expand Down Expand Up @@ -183,7 +182,7 @@ function searchItems(query, limit = 20) {
}

// Routes
app.get("/", (_req, res) => {
app.get("/health", (_req, res) => {
res.json({ message: "Welcome to the Boneset API" });
});

Expand Down Expand Up @@ -312,7 +311,7 @@ app.get("/api/bone-data/", async (req, res) => {
const imagesArray = descriptionData.images || [];
const images = imagesArray.map((filename) => ({
filename,
url: `${API_BASE_URL}/api/images/${encodeURIComponent(filename)}`,
url: `/api/images/${encodeURIComponent(filename)}`,
}));

res.json({
Expand Down Expand Up @@ -465,6 +464,13 @@ app.get("/api/search", searchLimiter, (req, res) => {
}
});

// Serve the frontend from the same origin as the API.
app.use(express.static(TEMPLATES_DIR));

app.get("/", (_req, res) => {
res.sendFile(path.join(TEMPLATES_DIR, "boneset.html"));
});

async function startServer() {
await initializeSearchCache();

Expand Down
15 changes: 11 additions & 4 deletions boneset-api/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ const request = require("supertest");
const { app } = require("./server");

describe("Initial configuration tests 263", () => {
it("should return 200 OK", async () => {
const response = await request(app).get("/");
it("should return 200 OK from health", async () => {
const response = await request(app).get("/health");
expect(response.statusCode).toBe(200);
});

it("should return message welcome", async () => {
const response = await request(app).get("/");
it("should return message welcome from health", async () => {
const response = await request(app).get("/health");
expect(response.body.message).toBe("Welcome to the Boneset API");
});

it("should serve the app HTML at /", async () => {
const response = await request(app).get("/");
expect(response.statusCode).toBe(200);
expect(response.headers["content-type"]).toMatch(/html/);
expect(response.text).toContain("Digital Bone Box");
});
});

// Unit tests for Issue 267: GET /api/annotations/:boneId
Expand Down
52 changes: 52 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"body-parser": "^1.20.6",
"cors": "^2.8.5",
"express": "^4.22.2",
"express-rate-limit": "^8.7.0",
"simple-git": "^3.36.0"
},
"devDependencies": {
Expand Down
6 changes: 3 additions & 3 deletions templates/boneset.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ <h1>Digital Bone Box</h1>
<label for="boneset-select">Boneset</label>
<select id="boneset-select"
name="boneId"
hx-get="http://127.0.0.1:8000/api/description/"
hx-get="/api/description/"
hx-target="#description-Container"
hx-trigger="change">
<option value="">Select a boneset</option>
Expand All @@ -92,7 +92,7 @@ <h1>Digital Bone Box</h1>
<label for="bone-select">Bone</label>
<select id="bone-select"
name="boneId"
hx-get="http://127.0.0.1:8000/api/description/"
hx-get="/api/description/"
hx-target="#description-Container"
hx-trigger="change"
disabled>
Expand All @@ -104,7 +104,7 @@ <h1>Digital Bone Box</h1>
<label for="subbone-select">Bone Part</label>
<select id="subbone-select"
name="boneId"
hx-get="http://127.0.0.1:8000/api/description/"
hx-get="/api/description/"
hx-target="#description-Container"
hx-trigger="change"
disabled>
Expand Down
2 changes: 1 addition & 1 deletion templates/js/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const API_CONFIG = {
BASE_URL: "http://127.0.0.1:8000",
BASE_URL: "",
ENDPOINTS: {
COMBINED_DATA: "/combined-data",
MOCK_BONE_DATA: "./js/mock-bone-data.json",
Expand Down
14 changes: 14 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"installCommand": "npm install && npm install --prefix boneset-api && rm -rf public && mkdir -p public/api && cp -R templates/. public/ && cp templates/boneset.html public/index.html && cp -R boneset-api/data/images public/api/images",
"rewrites": [
{
"source": "/(.*)",
"destination": "/api"
}
],
"functions": {
"api/index.js": {
"includeFiles": "{boneset-api/data/**,templates/**}"
}
}
}
Loading