Deno/Vite/React/TS template and schema.sql
This commit is contained in:
17
server/main.ts
Normal file
17
server/main.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Application } from "jsr:@oak/oak/application";
|
||||
import { Router } from "jsr:@oak/oak/router";
|
||||
import routeStaticFilesFrom from "./util/routeStaticFilesFrom.ts";
|
||||
|
||||
export const app = new Application();
|
||||
const router = new Router();
|
||||
|
||||
app.use(router.routes());
|
||||
app.use(routeStaticFilesFrom([
|
||||
`${Deno.cwd()}/client/dist`,
|
||||
`${Deno.cwd()}/client/public`,
|
||||
]));
|
||||
|
||||
if (import.meta.main) {
|
||||
console.log("Server listening on port http://localhost:8000");
|
||||
await app.listen({ port: 8000 });
|
||||
}
|
69
server/main_test.ts
Normal file
69
server/main_test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { assertEquals, assertExists } from "jsr:@std/assert";
|
||||
import { afterAll, beforeAll, describe, it } from "jsr:@std/testing/bdd";
|
||||
import { expect } from "jsr:@std/expect";
|
||||
import { Application } from "jsr:@oak/oak/application";
|
||||
import { Router } from "jsr:@oak/oak/router";
|
||||
|
||||
import { app } from "./main.ts";
|
||||
import routeStaticFilesFrom from "./util/routeStaticFilesFrom.ts";
|
||||
|
||||
describe("Application", () => {
|
||||
let serverInfo: { baseUrl: string; abortController: AbortController };
|
||||
beforeAll(async () => {
|
||||
console.log("Starting server");
|
||||
serverInfo = await serve();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
console.log("Shutting down server");
|
||||
serverInfo.abortController.abort();
|
||||
});
|
||||
|
||||
it("can be created", () => {
|
||||
assertExists(app);
|
||||
assertEquals(app instanceof Application, true);
|
||||
});
|
||||
|
||||
it("router accepts routes without throwing errors", () => {
|
||||
const router = new Router();
|
||||
app.use(router.routes());
|
||||
assertExists(router);
|
||||
});
|
||||
|
||||
it("can configure static routes", () => {
|
||||
const staticFileMiddleware = routeStaticFilesFrom([
|
||||
`${Deno.cwd()}/client/dist`,
|
||||
`${Deno.cwd()}/client/public`,
|
||||
]);
|
||||
app.use(staticFileMiddleware);
|
||||
assertExists(staticFileMiddleware);
|
||||
});
|
||||
|
||||
it("can request home page from running server", async () => {
|
||||
const response = await fetch(serverInfo.baseUrl);
|
||||
const body = await response.text();
|
||||
|
||||
assertEquals(response.status, 200);
|
||||
expect(body).toContain("<title>Vite + React + TS</title>");
|
||||
});
|
||||
});
|
||||
|
||||
async function serve(abortController = new AbortController()) {
|
||||
let randomPort = 0;
|
||||
|
||||
app.listen({ port: randomPort, signal: abortController.signal });
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
app.addEventListener("listen", (ev) => {
|
||||
randomPort = ev.port;
|
||||
console.log(`Server running on http://localhost:${ev.port}`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
baseUrl: `http://localhost:${randomPort}`,
|
||||
abortController: abortController,
|
||||
};
|
||||
}
|
||||
|
42
server/schema.sql
Normal file
42
server/schema.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- schema.sql
|
||||
|
||||
-- Create users table
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(255) NOT NULL UNIQUE,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
-- Create events table
|
||||
CREATE TABLE events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
host_user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
start_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
end_timestamp TIMESTAMP NOT NULL,
|
||||
status VARCHAR(10) CHECK (status IN ('active', 'inactive')) NOT NULL DEFAULT 'active'
|
||||
);
|
||||
|
||||
-- Create sessions table
|
||||
CREATE TABLE sessions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
event_id INTEGER NOT NULL REFERENCES events(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
start_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
end_timestamp TIMESTAMP NOT NULL,
|
||||
session_status VARCHAR(10) CHECK (session_status IN ('active', 'inactive')) NOT NULL DEFAULT 'active'
|
||||
);
|
||||
|
||||
-- Create photos table
|
||||
CREATE TABLE photos (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_id INTEGER NOT NULL REFERENCES sessions(id),
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
image_data BYTEA NOT NULL
|
||||
);
|
||||
|
||||
-- Create jwt_tokens table with indexes
|
||||
CREATE INDEX idx_jwt_tokens_username ON jwt_tokens (token_value);
|
||||
CREATE INDEX idx_jwt_tokens_event_name ON jwt_tokens (token_value);
|
19
server/util/routeStaticFilesFrom.ts
Normal file
19
server/util/routeStaticFilesFrom.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Next } from "jsr:@oak/oak/middleware";
|
||||
import { Context } from "jsr:@oak/oak/context";
|
||||
|
||||
// Configure static site routes so that we can serve
|
||||
// the Vite build output and the public folder
|
||||
export default function routeStaticFilesFrom(staticPaths: string[]) {
|
||||
return async (context: Context<Record<string, object>>, next: Next) => {
|
||||
for (const path of staticPaths) {
|
||||
try {
|
||||
await context.send({ root: path, index: "index.html" });
|
||||
return;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user