Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ typings/
.idea/

build/

/generated/prisma
12 changes: 10 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
"javascript.preferences.importModuleSpecifier": "relative",
"typescript.preferences.importModuleSpecifier": "relative",
"files.exclude": {
"node_modules": true
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"node_modules": true,
"**/.idea": true
},
"npm.packageManager": "yarn",
"files.eol": "\n",
"editor.insertSpaces": true,
"editor.tabSize": 4,
"typescript.enablePromptUseWorkspaceTsdk": true
"typescript.enablePromptUseWorkspaceTsdk": true,
"explorerExclude.backup": {}
}
26 changes: 16 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
"version": "2.1.1",
"type": "module",
"main": "./build/index.js",
"prisma": {
"seed": "tsx prisma/seed.ts"
},
"scripts": {
"cu": "docker compose up --build",
"cd": "docker compose down",
"start": "tsx ./index.ts | tee -a /var/log/datadrop/console.log",
"build": "rm -rf build/ && tsc",
"deploy:commands": "yarn build && node ./scripts/deploy-commands.cjs",
"lint": "biome check --write index.ts ./src ./scripts",
"env-gen": "node ./scripts/envgen.cjs"
"env-gen": "node ./scripts/envgen.cjs",
"prisma": "dotenvx run --convention=nextjs -- prisma"
},
"repository": {
"type": "git",
Expand All @@ -23,21 +27,23 @@
},
"homepage": "https://github.com/section-IG/DataDrop#readme",
"dependencies": {
"@dotenvx/dotenvx": "^1.51.0",
"@dotenvx/dotenvx": "^1.69.1",
"@hunteroi/advanced-logger": "^0.2.0",
"@hunteroi/discord-selfrole": "^4.0.5",
"@hunteroi/discord-temp-channels": "^3.3.1",
"@hunteroi/discord-verification": "^1.5.2",
"@prisma/client": "^7.8.0",
"discord-sync-commands": "^0.5.2",
"discord.js": "^14.22.1",
"nodemailer": "^7.0.6",
"ts-postgres": "1.3.0"
"discord.js": "^14.26.4",
"nodemailer": "^8.0.9",
"ts-postgres": "2.0.4"
},
"devDependencies": {
"@biomejs/biome": "^2.2.4",
"@types/node": "^24.5.2",
"@types/nodemailer": "^7.0.1",
"tsx": "^4.20.5",
"typescript": "^5.9.2"
"@biomejs/biome": "^2.4.16",
"@types/node": "^25.9.1",
"@types/nodemailer": "^8.0.0",
"prisma": "^7.8.0",
"tsx": "^4.22.3",
"typescript": "^6.0.3"
}
}
14 changes: 14 additions & 0 deletions prisma.config.ts
Original file line number Diff line number Diff line change
@@ -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"],
},
});
2 changes: 2 additions & 0 deletions prisma/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`yarn prisma migrate resolve --applied 0_init --preview-feature`
or `yarn prisma migrate dev`
60 changes: 60 additions & 0 deletions prisma/migrations/0_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "cron";

-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";

-- CreateExtension
CREATE EXTENSION IF NOT EXISTS "pg_cron";

-- CreateTable
CREATE TABLE "cron"."job" (
"jobid" BIGSERIAL NOT NULL,
"schedule" TEXT NOT NULL,
"command" TEXT NOT NULL,
"nodename" TEXT NOT NULL DEFAULT 'localhost',
"nodeport" INTEGER NOT NULL DEFAULT inet_server_port(),
"database" TEXT NOT NULL DEFAULT current_database(),
"username" TEXT NOT NULL DEFAULT CURRENT_USER,
"active" BOOLEAN NOT NULL DEFAULT true,
"jobname" TEXT,
CONSTRAINT "job_pkey" PRIMARY KEY ("jobid")
);

-- CreateTable
CREATE TABLE "cron"."job_run_details" (
"jobid" BIGINT,
"runid" BIGSERIAL NOT NULL,
"job_pid" INTEGER,
"database" TEXT,
"username" TEXT,
"command" TEXT,
"status" TEXT,
"return_message" TEXT,
"start_time" TIMESTAMPTZ(6),
"end_time" TIMESTAMPTZ(6),
CONSTRAINT "job_run_details_pkey" PRIMARY KEY ("runid")
);

-- CreateTable
CREATE TABLE "public"."users" (
"userid" TEXT NOT NULL,
"data" TEXT,
"code" TEXT,
"activatedcode" TEXT,
"activationtimestamp" TEXT,
"username" TEXT NOT NULL,
"status" INTEGER NOT NULL,
"nbcodecalled" INTEGER NOT NULL DEFAULT 0,
"nbverifycalled" INTEGER NOT NULL DEFAULT 0,
"createdat" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedat" TIMESTAMP(6),
"isdeleted" TIMESTAMP(6),
CONSTRAINT "users_pkey" PRIMARY KEY ("userid")
);

-- CreateIndex
CREATE UNIQUE INDEX "jobname_username_uniq" ON "cron"."job"("jobname", "username");

-- CreateIndex
CREATE UNIQUE INDEX "users_data_key" ON "public"."users"("data");
59 changes: 59 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}

datasource db {
provider = "postgresql"
extensions = [pg_cron]
schemas = ["cron", "public"]
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model job {
jobid BigInt @id @default(autoincrement())
schedule String
command String
nodename String @default("localhost")
nodeport Int @default(dbgenerated("inet_server_port()"))
database String @default(dbgenerated("current_database()"))
username String @default(dbgenerated("CURRENT_USER"))
active Boolean @default(true)
jobname String?

@@unique([jobname, username], map: "jobname_username_uniq")
@@schema("cron")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model job_run_details {
jobid BigInt?
runid BigInt @id @default(autoincrement())
job_pid Int?
database String?
username String?
command String?
status String?
return_message String?
start_time DateTime? @db.Timestamptz(6)
end_time DateTime? @db.Timestamptz(6)

@@schema("cron")
}

model users {
userid String @id
data String? @unique
code String?
activatedCode String?
activationTimestamp Int?
username String
status Int
nbCodeCalled Int @default(0)
nbVerifyCalled Int @default(0)
createdAt DateTime @default(now()) @db.Timestamp(6)
updatedAt DateTime @updatedAt @db.Timestamp(6)
isDeleted DateTime? @db.Timestamp(6)

@@schema("public")
}
28 changes: 28 additions & 0 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PrismaClient } from "@prisma/client";

const database = new PrismaClient();

async function main() {
database.job.upsert({
where: { jobid: 1 },
create: {
jobid: 1,
schedule: "0 0 * * *",
command:
"DELETE FROM Users WHERE isDeleted IS NOT NULL AND isDeleted < NOW() - INTERVAL '6 months'",
},
update: {
schedule: "0 0 * * *",
command:
"DELETE FROM Users WHERE isDeleted IS NOT NULL AND isDeleted < NOW() - INTERVAL '6 months'",
},
});
}

try {
await main();
} catch (e) {
console.error(e);
} finally {
await database.$disconnect();
}
35 changes: 16 additions & 19 deletions src/services/PostgresDatabaseService.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import type { ConsoleLogger } from "@hunteroi/advanced-logger";
import type { Snowflake } from "discord.js";
import {
Client,
type Client,
connect,
type DatabaseError,
type PreparedStatement,
type Value,
} from "ts-postgres";

import { getErrorMessage } from "../helpers.js";
import type { IDatabaseService, User } from "../models/index.js";

export class PostgresDatabaseService implements IDatabaseService {
readonly #logger: ConsoleLogger;
readonly #database: Client;
#database!: Client;

/**
* Creates an instance of DatabaseService.
Expand All @@ -21,21 +21,20 @@ export class PostgresDatabaseService implements IDatabaseService {
*/
constructor(logger: ConsoleLogger) {
this.#logger = logger;
this.#database = new Client({
}

/**
* @inherited
*/
public async start(): Promise<void> {
this.#database = await connect({
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
host: process.env.DATABASE_HOST,
port: Number(process.env.DATABASE_PORT),
database: process.env.POSTGRES_DB,
});
this.#listenToDatabaseEvents();
}

/**
* @inherited
*/
public async start(): Promise<void> {
await this.#database.connect();

await this.#database.query(`CREATE TABLE IF NOT EXISTS Migrations (
id serial PRIMARY KEY,
Expand Down Expand Up @@ -104,7 +103,7 @@ export class PostgresDatabaseService implements IDatabaseService {
*/
public async readBy(
argument: // biome-ignore lint/suspicious/noExplicitAny: DB values can be of any type
Map<string, any> | ((user: User, index: string | number) => boolean),
Map<string, any> | ((user: User, index: string | number) => boolean),
): Promise<User | undefined | null> {
if (!(argument instanceof Map))
throw new Error("Method not implemented.");
Expand Down Expand Up @@ -230,9 +229,6 @@ export class PostgresDatabaseService implements IDatabaseService {
}

#listenToDatabaseEvents() {
this.#database.on("connect", () =>
this.#logger.info("Connexion établie avec la base de données!"),
);
this.#database.on("end", () =>
this.#logger.info("Connexion fermée avec la base de données!"),
);
Expand Down Expand Up @@ -266,7 +262,7 @@ export class PostgresDatabaseService implements IDatabaseService {
}

// biome-ignore lint/suspicious/noExplicitAny: DB values can be of any type
#deconstructValues(values: [string, any][]): Value[] {
#deconstructValues(values: [string, any][]): any[] {
return values.flatMap(([, v]) =>
v instanceof Object && typeof v !== "bigint"
? JSON.stringify(v)
Expand All @@ -276,15 +272,16 @@ export class PostgresDatabaseService implements IDatabaseService {

async #executeStatement(
statement: PreparedStatement,
values: Value[] = [],
// biome-ignore lint/suspicious/noExplicitAny: DB values can be of any type
values: any[] = [],
isSelect = true,
): Promise<User | undefined | null> {
const notFoundMessage = "User not found";
try {
const entities = await statement.execute(values);
const result = await statement.execute(values);
if (!isSelect) return null;

const entity = [...entities].pop();
const entity = result.rows.at(-1);
if (!entity) throw new Error(notFoundMessage);

const asDate = (value: string | undefined | null): Date | null =>
Expand Down
33 changes: 16 additions & 17 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"strict": true,
"useUnknownInCatchVariables": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"rootDir": ".",
"outDir": "build",
"baseUrl": ".",
"resolvePackageJsonImports": true
}
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"strict": true,
"useUnknownInCatchVariables": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false,
"rootDir": ".",
"outDir": "build",
"resolvePackageJsonImports": true
}
}
Loading
Loading