Setup client + server framework

This commit is contained in:
thepaperpilot 2024-02-12 06:15:50 -06:00
parent 28e3a1f1e5
commit 7480da4b69
11 changed files with 189 additions and 18 deletions

2
.env.example Normal file
View file

@ -0,0 +1,2 @@
OAUTH_CLIENT_ID=
OAUTH_CLIENT_SECRET=

8
.gitignore vendored
View file

@ -39,4 +39,10 @@ yarn-error.log*
**/*.tgz
**/*.log
package-lock.json
**/*.bun
**/*.bun
.vscode
.env
db.sqlite

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "client"]
path = client
url = git@code.incremental.social:thepaperpilot/chromatic-lattice.git

17
Dockerfile Normal file
View file

@ -0,0 +1,17 @@
FROM oven/bun
WORKDIR /app
COPY package.json .
COPY bun.lockb .
RUN bun install --production
COPY src src
COPY tsconfig.json .
# COPY public public
ENV NODE_ENV production
CMD ["bun", "src/index.ts"]
EXPOSE 3000

BIN
bun.lockb Normal file

Binary file not shown.

1
client Submodule

@ -0,0 +1 @@
Subproject commit 337d394fd33496d9aff23ef847ce56b1464deee6

22
common/events.ts Normal file
View file

@ -0,0 +1,22 @@
import { t } from "elysia";
interface ServerToClientEvents {
"server version": (semver: string) => void;
info: (message: string) => void;
"set cursor position": (user: string, pos: { x: number; y: number }) => void;
chat: (user: string, message: string) => void;
}
export const clientToServerEvents = t.Object({
message: t.Union([
t.Object({
type: t.Literal("set cursor position"),
x: t.Number(),
y: t.Number()
}),
t.Object({
type: t.Literal("chat"),
messageq: t.String()
})
])
});

View file

@ -1,18 +1,24 @@
{
"name": "@bun-examples/elysia",
"version": "1.0.50",
"name": "chromatic-lattice",
"version": "1.0.0",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "bun run --watch src/index.ts"
"start": "bun run server/index.ts",
"dev": "conc npm:dev:server npm:dev:client",
"dev:server": "bun run --watch server/index.ts",
"dev:client": "bunx --bun vite client",
"build": "bunx --bun vite build client",
"test": "vitest run -r client",
"testw": "vitest -r client"
},
"dependencies": {
"elysia": "latest"
"@bogeychan/elysia-oauth2": "^0.0.19",
"elysia": "latest",
"profectus": "file:./client"
},
"devDependencies": {
"bun-types": "latest"
"bun-types": "latest",
"concurrently": "^8.2.2"
},
"module": "src/index.js",
"bun-create": {
"start": "bun run src/index.ts"
}
"module": "server/index.js"
}

109
server/index.ts Normal file
View file

@ -0,0 +1,109 @@
import { Elysia, t } from "elysia";
import oauth2 from "@bogeychan/elysia-oauth2";
import { Database } from "bun:sqlite";
import { randomBytes } from "crypto";
import { migrateDatabase } from "./migrations";
import { clientToServerEvents } from "../common/events";
const db = new Database("db.sqlite", { create: true });
migrateDatabase(db);
const states = new Set();
const auth = oauth2({
profiles: {
incsoc: {
provider: {
clientId: Bun.env.OAUTH_CLIENT_ID!,
clientSecret: Bun.env.OAUTH_CLIENT_SECRET!,
auth: {
url: 'https://auth.incremental.social/auth/v1',
params: {}
},
token: {
url: 'https://auth.incremental.social/oauth/v2/token',
params: {}
}
},
scope: ['email', 'profile']
}
},
state: {
check(ctx, id, state) {
if (states.has(state)) {
states.delete(state);
return true;
}
return false;
},
generate(ctx, id) {
const state = randomBytes(8).toString('hex');
states.add(state);
return state;
}
},
storage: {
get(ctx, id) {
console.log(`get token: ${id}`);
// const token = (
// db
// .query('SELECT token FROM storage WHERE uuid = ? AND id = ?')
// .get(uuid, id) as { token: string }
// )?.token;
// if (!token) {
// return;
// }
// return JSON.parse(token);
return undefined;
},
set(ctx, id, token) {
console.log(`new token: ${id}`);
// db.run(
// 'INSERT OR REPLACE INTO storage (id, token) VALUES (?, ?)',
// [id, JSON.stringify(token)]
// );
},
delete(ctx, id) {
// db.run('DELETE FROM storage WHERE id = ?', [id]);
}
}
});
const app = new Elysia()
.use(auth)
.ws('/ws', {
body: t.Object({
message: clientToServerEvents
}),
message(ws, { message }) {
console.log(message);
},
beforeHandle: async function({ set, authorized, tokenHeaders }) {
// Check auth
if (!(await authorized("incsoc"))) {
return (set.status = 'Unauthorized');
}
const user = await fetch('https://auth.incremental.social/oidc/v1/userinfo', {
headers: await tokenHeaders("incsoc")
});
console.log(JSON.stringify(user));
// Update avatar and display name from mbin, fallback to userinfo
}
})
.listen(3000);
console.log(
`🦊 Chromatic Lattice server is running at ${app.server?.url.href}`
);

12
server/migrations.ts Normal file
View file

@ -0,0 +1,12 @@
import { Database } from "bun:sqlite";
export function migrateDatabase(db: Database) {
const version = (db.query("PRAGMA user_version").get() as { user_version: number }).user_version;
if (version < 1) {
console.log("Applying migration 0 -> 1")
db.run("CREATE TABLE tokens (id TEXT, token TEXT, PRIMARY KEY(id))")
}
db.run("PRAGMA user_version = 1;");
}

View file

@ -1,7 +0,0 @@
import { Elysia } from "elysia";
const app = new Elysia().get("/", () => "Hello Elysia").listen(3000);
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);