Initial code

This commit is contained in:
Aslan 2025-12-22 21:58:15 -05:00
parent f4a1afe71b
commit c6d3e066c9
21 changed files with 2446 additions and 0 deletions

2
.gitignore vendored
View file

@ -130,3 +130,5 @@ dist
.yarn/install-state.gz
.pnp.*
/src/generated/prisma

15
docker-compose.yml Normal file
View file

@ -0,0 +1,15 @@
services:
postgres:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_USER: tetheruser
POSTGRES_PASSWORD: 2914400844
POSTGRES_DB: tetherdb
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql
volumes:
postgres_data:

1
env Normal file
View file

@ -0,0 +1 @@
DATABASE_URL="postgresql://tetheruser:password@localhost:5432/tetherdb"

2091
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

31
package.json Normal file
View file

@ -0,0 +1,31 @@
{
"name": "tether",
"version": "0.1.0",
"description": "Communication server using the Nexlink protocol",
"repository": {
"type": "git",
"url": "https://git.aslan2142.space/aslan/tether"
},
"license": "GPL-3.0-only",
"author": "",
"type": "module",
"main": "index.js",
"scripts": {
"build": "npx tsc",
"start": "npx tsc && node --env-file=.env dist/index.js"
},
"devDependencies": {
"@types/node": "^25.0.3",
"@types/pg": "^8.16.0",
"dotenv": "^17.2.3",
"prisma": "^7.2.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
},
"dependencies": {
"@prisma/adapter-pg": "^7.2.0",
"@prisma/client": "^7.2.0",
"fastify": "^5.6.2",
"pg": "^8.16.3"
}
}

14
prisma.config.ts Normal file
View file

@ -0,0 +1,14 @@
// This file was generated by Prisma, and assumes you have installed the following:
// npm install --save-dev prisma dotenv
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: process.env["DATABASE_URL"],
},
});

View file

@ -0,0 +1,11 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

View file

@ -0,0 +1,89 @@
/*
Warnings:
- The primary key for the `User` table will be changed. If it partially fails, the table could be left without primary key constraint.
- A unique constraint covering the columns `[id]` on the table `User` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[name]` on the table `User` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "User" DROP CONSTRAINT "User_pkey",
ADD COLUMN "admin" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "communityId" TEXT,
ADD COLUMN "description" TEXT,
ADD COLUMN "lastLogin" TIMESTAMP(3),
ADD COLUMN "passwordHash" TEXT,
ADD COLUMN "registerDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ALTER COLUMN "email" DROP NOT NULL,
ADD CONSTRAINT "User_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "User_id_seq";
-- CreateTable
CREATE TABLE "Community" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "Community_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Channel" (
"id" TEXT NOT NULL,
"name" TEXT,
"communityId" TEXT,
CONSTRAINT "Channel_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Role" (
"id" TEXT NOT NULL,
"name" TEXT,
"communityId" TEXT NOT NULL,
CONSTRAINT "Role_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Session" (
"id" TEXT NOT NULL,
"token" TEXT NOT NULL,
"userId" TEXT NOT NULL,
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Community_id_key" ON "Community"("id");
-- CreateIndex
CREATE UNIQUE INDEX "Community_name_key" ON "Community"("name");
-- CreateIndex
CREATE UNIQUE INDEX "Channel_id_key" ON "Channel"("id");
-- CreateIndex
CREATE UNIQUE INDEX "Role_id_key" ON "Role"("id");
-- CreateIndex
CREATE UNIQUE INDEX "Session_id_key" ON "Session"("id");
-- CreateIndex
CREATE UNIQUE INDEX "User_id_key" ON "User"("id");
-- CreateIndex
CREATE UNIQUE INDEX "User_name_key" ON "User"("name");
-- AddForeignKey
ALTER TABLE "Channel" ADD CONSTRAINT "Channel_communityId_fkey" FOREIGN KEY ("communityId") REFERENCES "Community"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Role" ADD CONSTRAINT "Role_communityId_fkey" FOREIGN KEY ("communityId") REFERENCES "Community"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "User" ADD CONSTRAINT "User_communityId_fkey" FOREIGN KEY ("communityId") REFERENCES "Community"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View file

@ -0,0 +1,17 @@
/*
Warnings:
- You are about to drop the column `name` on the `User` table. All the data in the column will be lost.
- A unique constraint covering the columns `[username]` on the table `User` will be added. If there are existing duplicate values, this will fail.
- Added the required column `username` to the `User` table without a default value. This is not possible if the table is not empty.
*/
-- DropIndex
DROP INDEX "User_name_key";
-- AlterTable
ALTER TABLE "User" DROP COLUMN "name",
ADD COLUMN "username" TEXT NOT NULL;
-- CreateIndex
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");

View file

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"

52
prisma/schema.prisma Normal file
View file

@ -0,0 +1,52 @@
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
engineType = "client"
}
datasource db {
provider = "postgresql"
}
model Community {
id String @id @unique @default(uuid())
name String @unique
members User[]
Channel Channel[]
Role Role[]
}
model Channel {
id String @id @unique @default(uuid())
name String?
community Community? @relation(fields: [communityId], references: [id])
communityId String?
}
model Role {
id String @id @unique @default(uuid())
name String?
community Community @relation(fields: [communityId], references: [id])
communityId String
}
model User {
id String @id @unique @default(uuid())
username String @unique
email String? @unique
passwordHash String?
description String?
admin Boolean @default(false)
registerDate DateTime @default(now())
lastLogin DateTime?
Community Community? @relation(fields: [communityId], references: [id])
communityId String?
Session Session[]
}
model Session {
id String @id @unique @default(uuid())
owner User @relation(fields: [userId], references: [id])
token String
userId String
}

4
src/config.json Normal file
View file

@ -0,0 +1,4 @@
{
"port": 3012,
"db": "db.json"
}

3
src/config.ts Normal file
View file

@ -0,0 +1,3 @@
import config from "./config.json" with { type: "json" };
export { config };

View file

@ -0,0 +1,2 @@
export * from "./test.js";
export * from "./routes.js";

View file

@ -0,0 +1,8 @@
import { type FastifyInstance } from "fastify";
import * as controller from "./test.js";
const testRoutes = async (fastify: FastifyInstance) => {
fastify.get("/test", controller.test);
};
export { testRoutes };

View file

@ -0,0 +1,10 @@
import { type FastifyReply, type FastifyRequest } from "fastify";
import { testdb } from "../../store/store.js";
const test = async (request: FastifyRequest, reply: FastifyReply) => {
testdb();
return [{ name: "Alice" }];
};
export { test };

15
src/index.ts Normal file
View file

@ -0,0 +1,15 @@
import { config } from "./config.js";
import Fastify from "fastify";
import { testRoutes } from "./controllers/test/routes.js";
const app = Fastify({
logger: true,
});
app.register(testRoutes);
app.listen({ port: config.port }, (err, address) => {
if (err) throw err;
console.log(`Server is now listening on ${address}`);
});

2
src/store/index.ts Normal file
View file

@ -0,0 +1,2 @@
export * from "./store.js";
export * from "./types.js";

27
src/store/store.ts Normal file
View file

@ -0,0 +1,27 @@
import { PrismaClient } from "../generated/prisma/client.js";
import { PrismaPg } from "@prisma/adapter-pg";
import { Pool } from "pg";
const connectionString = process.env.DATABASE_URL;
const pool = new Pool({ connectionString });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
async function testdb() {
const test = await prisma.user.findMany();
/*
const user = await prisma.user.create({
data: { name: "Alice", email: `alice${Math.random()}@example.com` },
});
console.log("Created user:", user);
const test = await prisma.user.findMany();
console.log(test);
*/
}
export { testdb };

3
src/store/types.ts Normal file
View file

@ -0,0 +1,3 @@
interface IState {}
export { type IState };

46
tsconfig.json Normal file
View file

@ -0,0 +1,46 @@
{
// Visit https://aka.ms/tsconfig to read more about this file
"compilerOptions": {
// File Layout
"rootDir": "./src",
"outDir": "./dist",
// Environment Settings
// See also https://aka.ms/tsconfig/module
"module": "nodenext",
"target": "esnext",
// For nodejs:
// "lib": ["esnext"],
"types": ["node"],
// and npm install -D @types/node
// Other Outputs
"sourceMap": true,
"declaration": true,
"declarationMap": true,
// Stricter Typechecking Options
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// Style Options
// "noImplicitReturns": true,
// "noImplicitOverride": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "noPropertyAccessFromIndexSignature": true,
// Recommended Options
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
"resolveJsonModule": true,
"esModuleInterop": true,
},
"exclude": ["node_modules", "prisma.config.ts", "prisma/**", "dist"],
}