diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a75dd10bf..8f54c119b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -59,6 +59,9 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
+ - name: Build scripts
+ run: pnpm run build:scripts
+
- name: Run script to bump version & copy files
run: pnpm run release
id: version-bump
diff --git a/apps/web/drizzle.config.ts b/apps/web/drizzle.config.ts
index 4adb6790d..0d630172b 100644
--- a/apps/web/drizzle.config.ts
+++ b/apps/web/drizzle.config.ts
@@ -1,16 +1,12 @@
-import { POSTGRES_ENVS } from '@/database/client';
-import { defineConfig } from 'drizzle-kit';
+import { defineVitNodeDrizzleConfig } from '@vitnode/core/drizzle.config';
-const { url, ...rest } = POSTGRES_ENVS;
+import { POSTGRES_URL, vitNodeApiConfig } from './src/vitnode.api.config';
-export default defineConfig({
- out: './src/database/migrations/',
- // schema: ['./src/plugins/**/database/schema/*'],
- schema: ['./src/database/schema/*'],
+export default defineVitNodeDrizzleConfig({
+ vitNodeApiConfig,
+ out: './migrations/',
dialect: 'postgresql',
- dbCredentials: process.env.POSTGRES_URL
- ? {
- url,
- }
- : rest,
+ dbCredentials: {
+ url: POSTGRES_URL,
+ },
});
diff --git a/apps/web/src/database/migrations/0000_funny_korath.sql b/apps/web/migrations/0000_wide_absorbing_man.sql
similarity index 100%
rename from apps/web/src/database/migrations/0000_funny_korath.sql
rename to apps/web/migrations/0000_wide_absorbing_man.sql
diff --git a/apps/web/src/database/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json
similarity index 99%
rename from apps/web/src/database/migrations/meta/0000_snapshot.json
rename to apps/web/migrations/meta/0000_snapshot.json
index 1ffbde22d..45a2d4ced 100644
--- a/apps/web/src/database/migrations/meta/0000_snapshot.json
+++ b/apps/web/migrations/meta/0000_snapshot.json
@@ -1,5 +1,5 @@
{
- "id": "fd378528-22f1-4e30-9108-4e06020776ba",
+ "id": "b2e3a2e7-f9c3-449a-92a9-8010647fe22e",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
diff --git a/apps/web/src/database/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json
similarity index 67%
rename from apps/web/src/database/migrations/meta/_journal.json
rename to apps/web/migrations/meta/_journal.json
index 3295cfd2a..eab4a41d6 100644
--- a/apps/web/src/database/migrations/meta/_journal.json
+++ b/apps/web/migrations/meta/_journal.json
@@ -5,8 +5,8 @@
{
"idx": 0,
"version": "7",
- "when": 1747245244787,
- "tag": "0000_funny_korath",
+ "when": 1748527899355,
+ "tag": "0000_wide_absorbing_man",
"breakpoints": true
}
]
diff --git a/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-blog)/blog/page.tsx b/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-blog)/blog/page.tsx
index c3458bc27..ec516ab95 100644
--- a/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-blog)/blog/page.tsx
+++ b/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-blog)/blog/page.tsx
@@ -1,6 +1,7 @@
+import { Link } from '@vitnode/core/lib/navigation';
+
import { Test } from '@vitnode/blog/views/test';
import { TestClient } from '@vitnode/blog/views/test/client';
-import { Link } from '@vitnode/core/lib/navigation';
export default function Page() {
return (
diff --git a/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx b/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx
index 291f8fbe6..62a57e214 100644
--- a/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx
+++ b/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx
@@ -1,8 +1,9 @@
import type { Metadata } from 'next/dist/types';
-import { SignInView } from '@vitnode/core/views/auth/sign-in/sign-in-view';
import { getTranslations } from 'next-intl/server';
+import { SignInView } from '@vitnode/core/views/auth/sign-in/sign-in-view';
+
export const generateMetadata = async ({
locale,
}: {
diff --git a/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx b/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx
index fa13dda37..65e261b4a 100644
--- a/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx
+++ b/apps/web/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx
@@ -1,8 +1,9 @@
import type { Metadata } from 'next/dist/types';
-import { SignUpView } from '@vitnode/core/views/auth/sign-up/sign-up-view';
import { getTranslations } from 'next-intl/server';
+import { SignUpView } from '@vitnode/core/views/auth/sign-up/sign-up-view';
+
export const generateMetadata = async ({
locale,
}: {
diff --git a/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx b/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx
new file mode 100644
index 000000000..dd1ff7e3d
--- /dev/null
+++ b/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx
@@ -0,0 +1,5 @@
+import { DashboardAdminView } from '@vitnode/core/views/admin/views/core/dashboard/dashboard-admin-view';
+
+export default function Page() {
+ return ;
+}
diff --git a/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/test/page.tsx b/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx
similarity index 100%
rename from apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/test/page.tsx
rename to apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx
diff --git a/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/users/page.tsx b/apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx
similarity index 100%
rename from apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/users/page.tsx
rename to apps/web/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx
diff --git a/apps/web/src/database/client.ts b/apps/web/src/database/client.ts
deleted file mode 100644
index ca02a67e9..000000000
--- a/apps/web/src/database/client.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as dotenv from 'dotenv';
-import { drizzle } from 'drizzle-orm/postgres-js';
-import { join } from 'path';
-
-dotenv.config({
- path: join(process.cwd(), '..', '..', '.env'),
-});
-
-export const POSTGRES_ENVS = {
- url:
- process.env.POSTGRES_URL ?? 'postgresql://root:root@localhost:5432/vitnode',
- host: process.env.POSTGRES_HOST ?? 'localhost',
- port: process.env.POSTGRES_PORT ? +process.env.POSTGRES_PORT : 5432,
- user: process.env.POSTGRES_USER ?? 'root',
- password: process.env.POSTGRES_PASSWORD ?? 'root',
- database: process.env.POSTGRES_NAME ?? 'vitnode',
- ssl: process.env.POSTGRES_SSL ? process.env.POSTGRES_SSL === 'true' : false,
-};
-
-export const dbClient = drizzle({
- connection: POSTGRES_ENVS.url,
- casing: 'camelCase',
-});
diff --git a/apps/web/src/vitnode.api.config.ts b/apps/web/src/vitnode.api.config.ts
index e977708b5..03fa90108 100644
--- a/apps/web/src/vitnode.api.config.ts
+++ b/apps/web/src/vitnode.api.config.ts
@@ -4,9 +4,25 @@ import { DiscordSSOApiPlugin } from '@vitnode/core/api/plugins/sso/discord';
import { FacebookSSOApiPlugin } from '@vitnode/core/api/plugins/sso/facebook';
import { GoogleSSOApiPlugin } from '@vitnode/core/api/plugins/sso/google';
import { buildApiConfig } from '@vitnode/core/vitnode.config';
+import { drizzle } from 'drizzle-orm/postgres-js';
+
+// import * as dotenv from 'dotenv';
+// import { drizzle } from 'drizzle-orm/postgres-js';
+// import { join } from 'path';
+
+// dotenv.config({
+// path: join(process.cwd(), '..', '..', '.env'),
+// });
+
+export const POSTGRES_URL =
+ process.env.POSTGRES_URL ?? 'postgresql://root:root@localhost:5432/vitnode';
export const vitNodeApiConfig = buildApiConfig({
plugins: [blogApiPlugin()],
+ dbProvider: drizzle({
+ connection: POSTGRES_URL,
+ casing: 'camelCase',
+ }),
emailProvider: NodemailerEmailPlugin({
from: process.env.NODE_MAILER_FROM,
host: process.env.NODE_MAILER_HOST,
diff --git a/bump-version.mjs b/bump-version.mjs
index 6398edd1d..6935ab7c5 100644
--- a/bump-version.mjs
+++ b/bump-version.mjs
@@ -365,6 +365,10 @@ class FileCopyManager {
from: path.join(sourcePath, '.gitignore'),
to: path.join(destPath, '.gitignore'),
},
+ {
+ from: path.join(sourcePath, 'drizzle.config.ts'),
+ to: path.join(destPath, 'drizzle.config.ts'),
+ },
];
for (const { from, to } of files) {
diff --git a/packages/create-vitnode-app/README.md b/packages/create-vitnode-app/README.md
new file mode 100644
index 000000000..06be4926c
--- /dev/null
+++ b/packages/create-vitnode-app/README.md
@@ -0,0 +1,38 @@
+# (VitNode) Create App
+
+This package is a CLI tool to create a new VitNode app quickly.
+
+Script based on [Create Next App](https://nextjs.org/).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Usage
+
+```bash
+npx create-vitnode-app@latest
+```
+
+or
+
+```bash
+pnpm create vitnode-app@latest
+```
+
+## Options
+
+| Option | Description |
+| ------------------- | ---------------------------------------------------------- |
+| `--package-manager` | Specify the package manager to use. Support `npm`, `pnpm`. |
+| `--eslint` | Initialize with eslint config. |
+| `--skip-install` | Skip installing packages after initializing the project. |
diff --git a/packages/vitnode/scripts/prepare-database.ts b/packages/vitnode/scripts/prepare-database.ts
index 765539044..2f079c952 100644
--- a/packages/vitnode/scripts/prepare-database.ts
+++ b/packages/vitnode/scripts/prepare-database.ts
@@ -1,17 +1,28 @@
/* eslint-disable no-console */
-import { dbClient } from '@/database/client.js';
-import { core_admin_permissions } from '@/database/schema/admins.js';
-import {
- core_languages,
- core_languages_words,
-} from '@/database/schema/languages.js';
-import { core_moderators_permissions } from '@/database/schema/moderators.js';
-import { core_roles } from '@/database/schema/roles.js';
+import { core_admin_permissions } from '@/database/admins.js';
+import { core_languages, core_languages_words } from '@/database/languages.js';
+import { core_moderators_permissions } from '@/database/moderators.js';
+import { core_roles } from '@/database/roles.js';
+import * as dotenv from 'dotenv';
import { count } from 'drizzle-orm';
+import { drizzle } from 'drizzle-orm/postgres-js';
+import { join } from 'path';
import { runInteractiveShellCommand } from './run-interactive-shell-command.js';
+dotenv.config({
+ path: join(process.cwd(), '..', '..', '.env'),
+});
+
+console.log(join(process.cwd(), '..', '..', '.env'));
+
+const dbClient = drizzle({
+ connection:
+ process.env.POSTGRES_URL ?? 'postgresql://root:root@localhost:5432/vitnode',
+ casing: 'camelCase',
+});
+
export const generateDatabaseMigrations = async () => {
try {
await runInteractiveShellCommand('npm', ['run', 'drizzle-kit', 'up']);
diff --git a/packages/vitnode/scripts/prepare/prepare-files.ts b/packages/vitnode/scripts/prepare/prepare-files.ts
index c4ef96fdb..3f7d88e66 100644
--- a/packages/vitnode/scripts/prepare/prepare-files.ts
+++ b/packages/vitnode/scripts/prepare/prepare-files.ts
@@ -1,51 +1,13 @@
/* eslint-disable no-console */
-import { existsSync } from 'fs';
-import { cp, mkdir } from 'fs/promises';
-import { join } from 'path';
-import { dirname } from 'path';
-import { fileURLToPath } from 'url';
import { preparePlugins } from './prepare-plugins';
-const __dirname = dirname(fileURLToPath(import.meta.url));
-
-const copyDatabase = async (pluginsPath: string) => {
- const databasePath = join(__dirname, '..', '..', 'src', 'database');
- if (!existsSync(databasePath)) {
- console.log(
- `⛔️ Database not found in 'src/database' directory. "${databasePath}"`,
- );
- process.exit(1);
- }
- const destinationPath = join(process.cwd(), 'src', 'database');
- if (!existsSync(destinationPath)) {
- await mkdir(destinationPath, { recursive: true });
- }
-
- // Copy files
- await cp(databasePath, destinationPath, {
- recursive: true,
- filter: src => !src.endsWith('client.ts'),
- });
-
- // Copy client.ts if it doesn't exist
- if (!existsSync(join(destinationPath, 'client.ts'))) {
- await cp(
- join(databasePath, 'client.ts'),
- join(destinationPath, 'client.ts'),
- );
- }
-};
-
export const prepareFiles = async ({
- pluginsPath,
initMessage,
}: {
initMessage: string;
- pluginsPath: string;
}) => {
console.log(`${initMessage} Preparing files...`);
- await copyDatabase(pluginsPath);
await preparePlugins();
console.log(`${initMessage} \x1b[32mFiles prepared successfully.\x1b[0m`);
process.exit(0);
diff --git a/packages/vitnode/scripts/scripts.ts b/packages/vitnode/scripts/scripts.ts
index f7ea7230f..8f0ff8b66 100644
--- a/packages/vitnode/scripts/scripts.ts
+++ b/packages/vitnode/scripts/scripts.ts
@@ -1,24 +1,11 @@
#!/usr/bin/env node
/* eslint-disable no-console */
-import { existsSync } from 'fs';
-import { join } from 'path';
import { processPlugin } from './plugin.js';
import { prepareDatabase } from './prepare-database.js';
import { prepareFiles } from './prepare/prepare-files.js';
const initMessage = '\x1b[34m[VitNode]\x1b[0m';
-const getPluginsPath = () => {
- const pluginsPath = join(process.cwd(), 'src', 'plugins');
- if (!existsSync(pluginsPath)) {
- console.log(
- `⛔️ Plugins not found in 'src/plugins' directory. "${pluginsPath}"`,
- );
- process.exit(1);
- }
-
- return pluginsPath;
-};
const command = process.argv[2];
const flag = process.argv[3];
@@ -35,7 +22,7 @@ switch (command) {
break;
case 'prepare':
- void prepareFiles({ pluginsPath: getPluginsPath(), initMessage });
+ void prepareFiles({ initMessage });
break;
default:
diff --git a/packages/vitnode/src/api/config.ts b/packages/vitnode/src/api/config.ts
index 7e3e3fa03..efb17649d 100644
--- a/packages/vitnode/src/api/config.ts
+++ b/packages/vitnode/src/api/config.ts
@@ -60,9 +60,16 @@ export function VitNodeAPI({
emailProvider: vitNodeApiConfig.emailProvider,
metadata: vitNodeConfig.metadata,
authorization: vitNodeApiConfig.authorization,
+ dbProvider: vitNodeApiConfig.dbProvider,
}),
);
- app.use('/*/admin/*', globalAdminMiddleware());
+ app.use(async (c, next) => {
+ if (c.req.path.includes('/admin/')) {
+ return globalAdminMiddleware()(c, next);
+ }
+
+ return next();
+ });
app.onError(error => {
if (error instanceof HTTPException) {
diff --git a/packages/vitnode/src/api/lib/get-user-ip.ts b/packages/vitnode/src/api/lib/get-user-ip.ts
index 1dde2b24b..70ac8ea9e 100644
--- a/packages/vitnode/src/api/lib/get-user-ip.ts
+++ b/packages/vitnode/src/api/lib/get-user-ip.ts
@@ -1,7 +1,8 @@
-import type { HonoRequest } from 'hono';
+import type { Context, Input } from 'hono';
+import type { Env } from 'hono';
-export const getUserIp = (req: HonoRequest): string => {
- const ip: string = req.header('x-forwarded-for')?.toString() ?? '0.0.0.0';
+export function getUserIp(c: Context): string {
+ const ip: string = c.req.header('x-forwarded-for')?.toString() ?? '0.0.0.0';
if (ip === '0.0.0.0') {
// eslint-disable-next-line no-console
@@ -11,4 +12,4 @@ export const getUserIp = (req: HonoRequest): string => {
}
return ip;
-};
+}
diff --git a/packages/vitnode/src/api/lib/with-pagination.ts b/packages/vitnode/src/api/lib/with-pagination.ts
index 5c360ad8f..11f1d33ab 100644
--- a/packages/vitnode/src/api/lib/with-pagination.ts
+++ b/packages/vitnode/src/api/lib/with-pagination.ts
@@ -5,8 +5,8 @@ import type {
PgTableWithColumns,
TableConfig,
} from 'drizzle-orm/pg-core';
+import type { Context, Env, Input } from 'hono';
-import { dbClient } from '@/database/client';
import { z } from '@hono/zod-openapi';
import { and, asc, count, desc, gt, lt } from 'drizzle-orm';
@@ -14,6 +14,7 @@ export async function withPagination<
QueryMin extends Record,
T extends TableConfig,
Primary extends ColumnBaseConfig<'number', string>,
+ E extends Env,
>({
query,
table,
@@ -21,7 +22,9 @@ export async function withPagination<
where: whereFromParams,
primaryCursor,
orderBy: orderByFromParams,
+ c,
}: {
+ c: Context;
orderBy: {
column: PgColumn;
order: 'asc' | 'desc';
@@ -103,7 +106,8 @@ export async function withPagination<
}
// Get total count
- const [{ count: totalCount }] = await dbClient
+ const [{ count: totalCount }] = await c
+ .get('db')
.select({ count: count() })
.from(table as PgTable)
.where(whereFromParams);
diff --git a/packages/vitnode/src/api/middlewares/global/global.ts b/packages/vitnode/src/api/middlewares/global/global.ts
index 56b299209..10fc37418 100644
--- a/packages/vitnode/src/api/middlewares/global/global.ts
+++ b/packages/vitnode/src/api/middlewares/global/global.ts
@@ -42,6 +42,7 @@ declare module 'hono' {
title: string;
};
};
+ db: Pick['dbProvider'];
deviceId: number;
user: null | {
avatarColor: string;
@@ -62,7 +63,8 @@ export const globalMiddleware = ({
authorization,
metadata,
emailProvider,
-}: Pick &
+ dbProvider,
+}: Pick &
Pick) => {
return async (c: Context, next: Next) => {
c.set('core', {
@@ -82,6 +84,7 @@ export const globalMiddleware = ({
cookieSecure: authorization?.cookieSecure ?? true,
},
});
+ c.set('db', dbProvider);
const deviceId = await new DeviceModel(c).getDeviceId();
c.set('deviceId', deviceId);
diff --git a/packages/vitnode/src/api/models/device.ts b/packages/vitnode/src/api/models/device.ts
index 447d64810..fe6393c12 100644
--- a/packages/vitnode/src/api/models/device.ts
+++ b/packages/vitnode/src/api/models/device.ts
@@ -1,7 +1,6 @@
import type { Context, Env, Input } from 'hono';
-import { dbClient } from '@/database/client';
-import { core_sessions_known_devices } from '@/database/schema/sessions';
+import { core_sessions_known_devices } from '@/database/sessions';
import { CONFIG } from '@/lib/config';
import { eq } from 'drizzle-orm';
import { getCookie, setCookie } from 'hono/cookie';
@@ -15,10 +14,11 @@ export class DeviceModel {
protected readonly c: Context;
private async createDevice() {
- const [device] = await dbClient
+ const [device] = await this.c
+ .get('db')
.insert(core_sessions_known_devices)
.values({
- ipAddress: getUserIp(this.c.req),
+ ipAddress: getUserIp(this.c),
userAgent: this.getUserAgent(),
})
.returning({ id: core_sessions_known_devices.id });
@@ -56,7 +56,8 @@ export class DeviceModel {
try {
if (deviceIdFromCookie) {
- const [device] = await dbClient
+ const [device] = await this.c
+ .get('db')
.select({
id: core_sessions_known_devices.id,
})
@@ -67,10 +68,11 @@ export class DeviceModel {
return await this.createDevice();
}
- await dbClient
+ await this.c
+ .get('db')
.update(core_sessions_known_devices)
.set({
- ipAddress: getUserIp(this.c.req),
+ ipAddress: getUserIp(this.c),
userAgent: this.getUserAgent(),
})
.where(eq(core_sessions_known_devices.id, deviceIdFromCookie));
diff --git a/packages/vitnode/src/api/models/session-admin.ts b/packages/vitnode/src/api/models/session-admin.ts
index bfeaea669..c768c8963 100644
--- a/packages/vitnode/src/api/models/session-admin.ts
+++ b/packages/vitnode/src/api/models/session-admin.ts
@@ -1,10 +1,6 @@
import type { Context, Env, Input } from 'hono';
-import { dbClient } from '@/database/client';
-import {
- core_admin_permissions,
- core_admin_sessions,
-} from '@/database/schema/admins';
+import { core_admin_permissions, core_admin_sessions } from '@/database/admins';
import { CONFIG } from '@/lib/config';
import { and, eq, gt, or } from 'drizzle-orm';
import { deleteCookie, getCookie, setCookie } from 'hono/cookie';
@@ -19,10 +15,11 @@ export class SessionAdminModel {
protected readonly c: Context;
async checkIfUserIsAdmin(userId: number) {
- const user = await new UserModel().getUserById(userId);
+ const user = await new UserModel().getUserById({ id: userId, c: this.c });
if (!user) return false;
- const [permission] = await dbClient
+ const [permission] = await this.c
+ .get('db')
.select()
.from(core_admin_permissions)
.where(
@@ -48,14 +45,17 @@ export class SessionAdminModel {
.join('');
const deviceId = this.c.get('deviceId');
- await dbClient.insert(core_admin_sessions).values({
- token,
- userId,
- expiresAt: new Date(
- Date.now() + this.c.get('core').authorization.adminCookieExpires,
- ),
- deviceId,
- });
+ await this.c
+ .get('db')
+ .insert(core_admin_sessions)
+ .values({
+ token,
+ userId,
+ expiresAt: new Date(
+ Date.now() + this.c.get('core').authorization.adminCookieExpires,
+ ),
+ deviceId,
+ });
setCookie(this.c, this.c.get('core').authorization.adminCookieName, token, {
httpOnly: true,
@@ -78,7 +78,8 @@ export class SessionAdminModel {
);
if (!token) return;
- await dbClient
+ await this.c
+ .get('db')
.delete(core_admin_sessions)
.where(eq(core_admin_sessions.token, token));
deleteCookie(this.c, this.c.get('core').authorization.adminCookieName, {
@@ -93,7 +94,8 @@ export class SessionAdminModel {
const deviceId = this.c.get('deviceId');
if (!deviceId) return null;
- const [session] = await dbClient
+ const [session] = await this.c
+ .get('db')
.select({
token: core_admin_sessions.token,
userId: core_admin_sessions.userId,
@@ -109,7 +111,10 @@ export class SessionAdminModel {
.limit(1);
if (!session) return null;
- const user = await new UserModel().getUserById(session.userId);
+ const user = await new UserModel().getUserById({
+ id: session.userId,
+ c: this.c,
+ });
if (!user) return null;
return user;
diff --git a/packages/vitnode/src/api/models/session.ts b/packages/vitnode/src/api/models/session.ts
index 2a34d635a..13eb48e7b 100644
--- a/packages/vitnode/src/api/models/session.ts
+++ b/packages/vitnode/src/api/models/session.ts
@@ -1,7 +1,6 @@
import type { Context, Env, Input } from 'hono';
-import { dbClient } from '@/database/client';
-import { core_sessions } from '@/database/schema/sessions';
+import { core_sessions } from '@/database/sessions';
import { CONFIG } from '@/lib/config';
import { and, eq, gt } from 'drizzle-orm';
import { deleteCookie, getCookie, setCookie } from 'hono/cookie';
@@ -23,14 +22,17 @@ export class SessionModel {
.join('');
const deviceId = this.c.get('deviceId');
- await dbClient.insert(core_sessions).values({
- token,
- userId,
- expiresAt: new Date(
- Date.now() + this.c.get('core').authorization.cookie_expires,
- ),
- deviceId,
- });
+ await this.c
+ .get('db')
+ .insert(core_sessions)
+ .values({
+ token,
+ userId,
+ expiresAt: new Date(
+ Date.now() + this.c.get('core').authorization.cookie_expires,
+ ),
+ deviceId,
+ });
setCookie(this.c, this.c.get('core').authorization.cookieName, token, {
httpOnly: true,
@@ -55,7 +57,10 @@ export class SessionModel {
);
if (!token) return;
- await dbClient.delete(core_sessions).where(eq(core_sessions.token, token));
+ await this.c
+ .get('db')
+ .delete(core_sessions)
+ .where(eq(core_sessions.token, token));
deleteCookie(this.c, this.c.get('core').authorization.cookieName);
}
@@ -68,7 +73,8 @@ export class SessionModel {
const deviceId = this.c.get('deviceId');
if (!deviceId) return null;
- const [session] = await dbClient
+ const [session] = await this.c
+ .get('db')
.select({
token: core_sessions.token,
userId: core_sessions.userId,
@@ -86,7 +92,10 @@ export class SessionModel {
if (!session || session.token !== token) {
return null;
}
- const user = await new UserModel().getUserById(session.userId);
+ const user = await new UserModel().getUserById({
+ id: session.userId,
+ c: this.c,
+ });
if (!user) return null;
return user;
diff --git a/packages/vitnode/src/api/models/sso.ts b/packages/vitnode/src/api/models/sso.ts
index 09c53d130..2c8a15c42 100644
--- a/packages/vitnode/src/api/models/sso.ts
+++ b/packages/vitnode/src/api/models/sso.ts
@@ -1,7 +1,6 @@
import type { Context, Env, Input } from 'hono';
-import { dbClient } from '@/database/client';
-import { core_users, core_users_sso } from '@/database/schema/users';
+import { core_users, core_users_sso } from '@/database/users';
import { CONFIG } from '@/lib/config';
import { removeSpecialCharacters } from '@/lib/special-characters';
import crypto from 'crypto';
@@ -56,9 +55,9 @@ export class SSOModel {
newsletter: false,
hashedPassword: undefined,
},
- c.req,
+ c,
);
- await dbClient.insert(core_users_sso).values({
+ await c.get('db').insert(core_users_sso).values({
userId: data.id,
providerId: providerId,
providerAccountId: user.id,
@@ -87,7 +86,7 @@ export class SSOModel {
const ssoToken = await provider.fetchToken(code);
const userFromSSO = await provider.fetchUser(ssoToken);
- return await dbClient.transaction(async tx => {
+ return await this.c.get('db').transaction(async tx => {
const [dataSSOFromDb] = await tx
.select({
userId: core_users_sso.userId,
diff --git a/packages/vitnode/src/api/models/user/get-user-by-id.ts b/packages/vitnode/src/api/models/user/get-user-by-id.ts
index 8befccb8b..b2db3cb6c 100644
--- a/packages/vitnode/src/api/models/user/get-user-by-id.ts
+++ b/packages/vitnode/src/api/models/user/get-user-by-id.ts
@@ -1,9 +1,17 @@
-import { dbClient } from '@/database/client';
-import { core_users } from '@/database/schema/users';
+import type { Context, Env, Input } from 'hono';
+
+import { core_users } from '@/database/users';
import { eq } from 'drizzle-orm';
-export const getUserById = async (id: number) => {
- const [user] = await dbClient
+export async function getUserById({
+ id,
+ c,
+}: {
+ c: Context;
+ id: number;
+}) {
+ const [user] = await c
+ .get('db')
.select({
id: core_users.id,
email: core_users.email,
@@ -22,4 +30,4 @@ export const getUserById = async (id: number) => {
if (!user) return null;
return user;
-};
+}
diff --git a/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts b/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts
index 93ae7f9bf..55f95f4e6 100644
--- a/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts
+++ b/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts
@@ -1,5 +1,7 @@
-import { dbClient } from '@/database/client';
-import { core_users } from '@/database/schema/users';
+import type { Context, Input } from 'hono';
+import type { Env } from 'hono';
+
+import { core_users } from '@/database/users';
import { eq } from 'drizzle-orm';
import { HTTPException } from 'hono/http-exception';
@@ -8,11 +10,14 @@ import { PasswordModel } from '../password';
export const signInWithPassword = async ({
email,
password,
+ c,
}: {
+ c: Context;
email: string;
password: string;
}) => {
- const [user] = await dbClient
+ const [user] = await c
+ .get('db')
.select({
id: core_users.id,
email: core_users.email,
diff --git a/packages/vitnode/src/api/models/user/sign-up.ts b/packages/vitnode/src/api/models/user/sign-up.ts
index 5c81881d0..af2d72980 100644
--- a/packages/vitnode/src/api/models/user/sign-up.ts
+++ b/packages/vitnode/src/api/models/user/sign-up.ts
@@ -1,25 +1,28 @@
-import type { HonoRequest } from 'hono';
+import type { Context, Env, Input } from 'hono';
import { getUserIp } from '@/api/lib/get-user-ip';
import { generateAvatarColor } from '@/api/modules/users/avatar-color';
-import { dbClient } from '@/database/client';
-import { core_roles } from '@/database/schema/roles';
-import { core_users } from '@/database/schema/users';
+import { core_roles } from '@/database/roles';
+import { core_users } from '@/database/users';
import { removeSpecialCharacters } from '@/lib/special-characters';
import { and, count, eq, or } from 'drizzle-orm';
import { HTTPException } from 'hono/http-exception';
-const getDefaultData = async (): Promise<{
+const getDefaultData = async (
+ c: Context,
+): Promise<{
emailVerified: boolean;
roleId: number;
}> => {
- const [countUsers] = await dbClient
+ const [countUsers] = await c
+ .get('db')
.select({ count: count() })
.from(core_users);
// If no users, return root group
if (countUsers.count === 0) {
- const [defaultRole] = await dbClient
+ const [defaultRole] = await c
+ .get('db')
.select({
id: core_roles.id,
})
@@ -39,7 +42,8 @@ const getDefaultData = async (): Promise<{
};
}
- const [defaultRole] = await dbClient
+ const [defaultRole] = await c
+ .get('db')
.select({
id: core_roles.id,
})
@@ -72,10 +76,11 @@ export const signUp = async (
name: string;
newsletter?: boolean;
},
- req: HonoRequest,
+ c: Context,
) => {
const convertToNameSEO = removeSpecialCharacters(name);
- const checkIfUserExist = await dbClient
+ const checkIfUserExist = await c
+ .get('db')
.select({
email: core_users.email,
name_code: core_users.nameCode,
@@ -103,8 +108,9 @@ export const signUp = async (
});
}
- const { roleId, emailVerified } = await getDefaultData();
- const [data] = await dbClient
+ const { roleId, emailVerified } = await getDefaultData(c);
+ const [data] = await c
+ .get('db')
.insert(core_users)
.values({
email,
@@ -116,7 +122,7 @@ export const signUp = async (
avatarColor: generateAvatarColor(name),
roleId,
emailVerified,
- ipAddress: getUserIp(req),
+ ipAddress: getUserIp(c),
// TODO: Handle language
// language: await this.getLanguage(req),
})
diff --git a/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts b/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts
index 3efa998f9..360c5c76b 100644
--- a/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts
+++ b/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts
@@ -4,8 +4,7 @@ import {
zodPaginationPageInfo,
zodPaginationQuery,
} from '@/api/lib/with-pagination';
-import { dbClient } from '@/database/client';
-import { core_users } from '@/database/schema/users';
+import { core_users } from '@/database/users';
import { z } from '@hono/zod-openapi';
export const listUsersAdminRoute = buildRoute({
@@ -56,7 +55,8 @@ export const listUsersAdminRoute = buildRoute({
},
primaryCursor: core_users.id,
query: async ({ limit, where, orderBy }) =>
- await dbClient
+ await c
+ .get('db')
.select({
id: core_users.id,
name: core_users.name,
@@ -81,6 +81,7 @@ export const listUsersAdminRoute = buildRoute({
: core_users.createdAt,
order: query.order ?? 'desc',
},
+ c,
});
return c.json(data);
diff --git a/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts b/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts
index 72545e7c0..518a27856 100644
--- a/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts
+++ b/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts
@@ -4,8 +4,7 @@ import {
zodPaginationPageInfo,
zodPaginationQuery,
} from '@/api/lib/with-pagination';
-import { dbClient } from '@/database/client';
-import { core_users } from '@/database/schema/users';
+import { core_users } from '@/database/users';
import { z } from '@hono/zod-openapi';
export const usersAdminRoute = buildRoute({
@@ -59,7 +58,8 @@ export const usersAdminRoute = buildRoute({
},
primaryCursor: core_users.id,
query: async ({ limit, where, orderBy }) =>
- await dbClient
+ await c
+ .get('db')
.select({
id: core_users.id,
name: core_users.name,
@@ -84,6 +84,7 @@ export const usersAdminRoute = buildRoute({
: core_users.createdAt,
order: query.order ?? 'desc',
},
+ c,
});
return c.json(data);
diff --git a/packages/vitnode/src/api/modules/middleware/test.ts b/packages/vitnode/src/api/modules/middleware/test.ts
index b2f65987e..526830be2 100644
--- a/packages/vitnode/src/api/modules/middleware/test.ts
+++ b/packages/vitnode/src/api/modules/middleware/test.ts
@@ -1,6 +1,5 @@
import { buildRoute } from '@/api/lib/route';
-import { dbClient } from '@/database/client';
-import { core_test } from '@/database/schema/test';
+import { core_test } from '@/database/test';
import { z } from 'zod';
import {
@@ -43,7 +42,8 @@ export const routeTestMiddleware = buildRoute({
},
primaryCursor: core_test.id,
query: async ({ limit, where, orderBy }) =>
- await dbClient
+ await c
+ .get('db')
.select()
.from(core_test)
.where(where)
@@ -54,6 +54,7 @@ export const routeTestMiddleware = buildRoute({
column: query.orderBy ? core_test[query.orderBy] : core_test.createdAt,
order: query.order ?? 'desc',
},
+ c,
});
return c.json(data);
diff --git a/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts b/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts
index 85b49b1df..a739d6322 100644
--- a/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts
+++ b/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts
@@ -49,7 +49,11 @@ export const signInRoute = buildRoute({
},
handler: async c => {
const { password, isAdmin, email } = c.req.valid('json');
- const data = await new UserModel().signInWithPassword({ password, email });
+ const data = await new UserModel().signInWithPassword({
+ password,
+ email,
+ c,
+ });
if (isAdmin) {
const { token } = await new SessionAdminModel(c).createSessionByUserId(
diff --git a/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts b/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts
index cad27f562..a68aee5ec 100644
--- a/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts
+++ b/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts
@@ -55,7 +55,7 @@ export const signUpRoute = buildRoute({
);
const data = await new UserModel().signUp(
{ ...c.req.valid('json'), hashedPassword },
- c.req,
+ c,
);
return c.json({ id: data.id });
diff --git a/packages/vitnode/src/app_admin/core/page.tsx b/packages/vitnode/src/app_admin/core/page.tsx
new file mode 100644
index 000000000..331370fe7
--- /dev/null
+++ b/packages/vitnode/src/app_admin/core/page.tsx
@@ -0,0 +1,5 @@
+import { DashboardAdminView } from '../../views/admin/views/core/dashboard/dashboard-admin-view';
+
+export default function Page() {
+ return ;
+}
diff --git a/packages/vitnode/src/app_admin/core/test/page.tsx b/packages/vitnode/src/app_admin/core/test/page.tsx
new file mode 100644
index 000000000..0f9a918b8
--- /dev/null
+++ b/packages/vitnode/src/app_admin/core/test/page.tsx
@@ -0,0 +1,5 @@
+import { TestView } from '../../../views/admin/views/core/test';
+
+export default function Page() {
+ return ;
+}
diff --git a/packages/vitnode/src/app_admin/users/page.tsx b/packages/vitnode/src/app_admin/core/users/page.tsx
similarity index 59%
rename from packages/vitnode/src/app_admin/users/page.tsx
rename to packages/vitnode/src/app_admin/core/users/page.tsx
index f73488f26..7610deca4 100644
--- a/packages/vitnode/src/app_admin/users/page.tsx
+++ b/packages/vitnode/src/app_admin/core/users/page.tsx
@@ -1,4 +1,4 @@
-import { UsersAdminView } from '../../views/admin/views/core/users/users-admin-view';
+import { UsersAdminView } from '../../../views/admin/views/core/users/users-admin-view';
export default function Page(
props: React.ComponentProps,
diff --git a/packages/vitnode/src/app_admin/page.tsx b/packages/vitnode/src/app_admin/page.tsx
deleted file mode 100644
index 0ba052e39..000000000
--- a/packages/vitnode/src/app_admin/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { DashboardAdminView } from '../views/admin/views/core/dashboard/dashboard-admin-view';
-
-export default function Page() {
- return ;
-}
diff --git a/packages/vitnode/src/app_admin/test/page.tsx b/packages/vitnode/src/app_admin/test/page.tsx
deleted file mode 100644
index 537c4e896..000000000
--- a/packages/vitnode/src/app_admin/test/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { TestView } from '../../views/admin/views/core/test';
-
-export default function Page() {
- return ;
-}
diff --git a/packages/vitnode/src/components/form/fields/combobox.tsx b/packages/vitnode/src/components/form/fields/combobox.tsx
index 1ddc3c085..57a44bab1 100644
--- a/packages/vitnode/src/components/form/fields/combobox.tsx
+++ b/packages/vitnode/src/components/form/fields/combobox.tsx
@@ -31,7 +31,8 @@ export function AutoFormCombobox({
field,
description,
shape,
- placeholder,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ placeholder: _,
className,
labels = [],
...props
diff --git a/apps/web/src/database/schema/admins.ts b/packages/vitnode/src/database/admins.ts
similarity index 100%
rename from apps/web/src/database/schema/admins.ts
rename to packages/vitnode/src/database/admins.ts
diff --git a/packages/vitnode/src/database/client.ts b/packages/vitnode/src/database/client.ts
deleted file mode 100644
index 62ff46b9f..000000000
--- a/packages/vitnode/src/database/client.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as dotenv from 'dotenv';
-import { drizzle } from 'drizzle-orm/postgres-js';
-import { join } from 'path';
-
-dotenv.config({
- path: join(process.cwd(), '..', '..', '.env'),
-});
-
-export const POSTGRES_ENVS = {
- url:
- process.env.POSTGRES_URL ?? 'postgresql://root:root@localhost:5432/vitnode',
- host: process.env.POSTGRES_HOST ?? 'localhost',
- port: process.env.POSTGRES_PORT ? +process.env.POSTGRES_PORT : 5432,
- user: process.env.POSTGRES_USER ?? 'root',
- password: process.env.POSTGRES_PASSWORD ?? 'root',
- database: process.env.POSTGRES_NAME ?? 'vitnode',
- ssl: process.env.POSTGRES_SSL ? process.env.POSTGRES_SSL === 'true' : false,
-};
-
-export const dbClient = drizzle({
- connection: process.env.POSTGRES_URL ? POSTGRES_ENVS.url : POSTGRES_ENVS,
- casing: 'camelCase',
-});
diff --git a/apps/web/src/database/schema/languages.ts b/packages/vitnode/src/database/languages.ts
similarity index 100%
rename from apps/web/src/database/schema/languages.ts
rename to packages/vitnode/src/database/languages.ts
diff --git a/apps/web/src/database/schema/moderators.ts b/packages/vitnode/src/database/moderators.ts
similarity index 100%
rename from apps/web/src/database/schema/moderators.ts
rename to packages/vitnode/src/database/moderators.ts
diff --git a/apps/web/src/database/schema/roles.ts b/packages/vitnode/src/database/roles.ts
similarity index 100%
rename from apps/web/src/database/schema/roles.ts
rename to packages/vitnode/src/database/roles.ts
diff --git a/packages/vitnode/src/database/schema/admins.ts b/packages/vitnode/src/database/schema/admins.ts
deleted file mode 100644
index 2773083c6..000000000
--- a/packages/vitnode/src/database/schema/admins.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { relations } from 'drizzle-orm';
-import { index, pgTable } from 'drizzle-orm/pg-core';
-
-import { core_roles } from './roles';
-import { core_sessions_known_devices } from './sessions';
-import { core_users } from './users';
-
-export const core_admin_permissions = pgTable(
- 'core_admin_permissions',
- t => ({
- id: t.serial().primaryKey(),
- roleId: t.integer().references(() => core_roles.id, {
- onDelete: 'cascade',
- }),
- userId: t.integer().references(() => core_users.id, {
- onDelete: 'cascade',
- }),
- createdAt: t.timestamp().notNull().defaultNow(),
- updatedAt: t
- .timestamp()
- .notNull()
- .$onUpdate(() => new Date()),
- protected: t.boolean().notNull().default(false),
- // data: t.jsonb().$type<{ permissions: PermissionsStaffArgs[] }>().default({
- // permissions: [],
- // }),
- }),
- t => [
- index('core_admin_permissions_role_id_idx').on(t.roleId),
- index('core_admin_permissions_user_id_idx').on(t.userId),
- ],
-).enableRLS();
-
-export const core_admin_permissions_relations = relations(
- core_admin_permissions,
- ({ one }) => ({
- group: one(core_roles, {
- fields: [core_admin_permissions.roleId],
- references: [core_roles.id],
- }),
- user: one(core_users, {
- fields: [core_admin_permissions.userId],
- references: [core_users.id],
- }),
- }),
-);
-
-export const core_admin_sessions = pgTable(
- 'core_admin_sessions',
- t => ({
- id: t.serial().primaryKey(),
- token: t.varchar({ length: 255 }).notNull().unique(),
- userId: t
- .integer()
- .notNull()
- .references(() => core_users.id, {
- onDelete: 'cascade',
- }),
- createdAt: t.timestamp().notNull().defaultNow(),
- lastSeen: t.timestamp().notNull().defaultNow(),
- expiresAt: t.timestamp().notNull(),
- deviceId: t
- .integer()
- .references(() => core_sessions_known_devices.id, {
- onDelete: 'cascade',
- })
- .notNull(),
- }),
- t => [
- index('core_admin_sessions_token_idx').on(t.token),
- index('core_admin_sessions_user_id_idx').on(t.userId),
- ],
-).enableRLS();
-
-export const core_admin_sessions_relations = relations(
- core_admin_sessions,
- ({ one }) => ({
- user: one(core_users, {
- fields: [core_admin_sessions.userId],
- references: [core_users.id],
- }),
- device: one(core_sessions_known_devices, {
- fields: [core_admin_sessions.deviceId],
- references: [core_sessions_known_devices.id],
- }),
- }),
-);
diff --git a/packages/vitnode/src/database/schema/languages.ts b/packages/vitnode/src/database/schema/languages.ts
deleted file mode 100644
index f7dd32998..000000000
--- a/packages/vitnode/src/database/schema/languages.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { relations } from 'drizzle-orm';
-import { index, pgTable } from 'drizzle-orm/pg-core';
-
-export const core_languages = pgTable(
- 'core_languages',
- t => ({
- id: t.serial().primaryKey(),
- code: t.varchar({ length: 32 }).notNull().unique(),
- name: t.varchar({ length: 255 }).notNull(),
- timezone: t.varchar({ length: 255 }).notNull().default('UTC'),
- protected: t.boolean().notNull().default(false),
- default: t.boolean().notNull().default(false),
- enabled: t.boolean().notNull().default(true),
- createdAt: t.timestamp().notNull().defaultNow(),
- updatedAt: t
- .timestamp()
- .notNull()
- .$onUpdate(() => new Date()),
- time24: t.boolean().notNull().default(false),
- }),
- t => [
- index('core_languages_code_idx').on(t.code),
- index('core_languages_name_idx').on(t.name),
- ],
-).enableRLS();
-
-export const core_languages_words = pgTable(
- 'core_languages_words',
- t => ({
- id: t.serial().primaryKey(),
- languageCode: t
- .varchar()
- .notNull()
- .references(() => core_languages.code, {
- onDelete: 'cascade',
- }),
- pluginCode: t.varchar({ length: 50 }).notNull(),
- itemId: t.integer().notNull(),
- value: t.text().notNull(),
- tableName: t.varchar({ length: 255 }).notNull(),
- variable: t.varchar({ length: 255 }).notNull(),
- }),
- t => [index('core_languages_words_lang_code_idx').on(t.languageCode)],
-).enableRLS();
-
-export const core_languages_words_relations = relations(
- core_languages_words,
- ({ one }) => ({
- language: one(core_languages, {
- fields: [core_languages_words.languageCode],
- references: [core_languages.code],
- }),
- }),
-);
diff --git a/packages/vitnode/src/database/schema/moderators.ts b/packages/vitnode/src/database/schema/moderators.ts
deleted file mode 100644
index 5337c5ca4..000000000
--- a/packages/vitnode/src/database/schema/moderators.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { relations } from 'drizzle-orm';
-import { index, pgTable } from 'drizzle-orm/pg-core';
-
-import { core_roles } from './roles';
-import { core_users } from './users';
-
-export const core_moderators_permissions = pgTable(
- 'core_moderators_permissions',
- t => ({
- id: t.serial().primaryKey(),
- roleId: t.integer().references(() => core_roles.id, {
- onDelete: 'cascade',
- }),
- userId: t.integer().references(() => core_users.id, {
- onDelete: 'cascade',
- }),
- createdAt: t.timestamp().notNull().defaultNow(),
- updatedAt: t
- .timestamp()
- .notNull()
- .$onUpdate(() => new Date()),
- protected: t.boolean().notNull().default(false),
- }),
- t => [
- index('core_moderators_permissions_role_id_idx').on(t.roleId),
- index('core_moderators_permissions_user_id_idx').on(t.userId),
- ],
-).enableRLS();
-
-export const core_moderators_permissions_relations = relations(
- core_moderators_permissions,
- ({ one }) => ({
- group: one(core_roles, {
- fields: [core_moderators_permissions.roleId],
- references: [core_roles.id],
- }),
- user: one(core_users, {
- fields: [core_moderators_permissions.userId],
- references: [core_users.id],
- }),
- }),
-);
diff --git a/packages/vitnode/src/database/schema/roles.ts b/packages/vitnode/src/database/schema/roles.ts
deleted file mode 100644
index 50a71d510..000000000
--- a/packages/vitnode/src/database/schema/roles.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { pgTable } from 'drizzle-orm/pg-core';
-
-export const core_roles = pgTable('core_roles', t => ({
- id: t.serial().primaryKey(),
- createdAt: t.timestamp().notNull().defaultNow(),
- updatedAt: t
- .timestamp()
- .notNull()
- .$onUpdate(() => new Date()),
- protected: t.boolean().notNull().default(false),
- default: t.boolean().notNull().default(false),
- root: t.boolean().notNull().default(false),
- guest: t.boolean().notNull().default(false),
- color: t.varchar({ length: 19 }),
-})).enableRLS();
diff --git a/packages/vitnode/src/database/schema/sessions.ts b/packages/vitnode/src/database/schema/sessions.ts
deleted file mode 100644
index 5506b2f4c..000000000
--- a/packages/vitnode/src/database/schema/sessions.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { relations } from 'drizzle-orm';
-import { index, pgTable } from 'drizzle-orm/pg-core';
-
-import { core_users } from './users';
-
-export const core_sessions = pgTable(
- 'core_sessions',
- t => ({
- id: t.serial().primaryKey(),
- token: t.varchar({ length: 255 }).notNull().unique(),
- userId: t
- .integer()
- .notNull()
- .references(() => core_users.id, {
- onDelete: 'cascade',
- }),
- createdAt: t.timestamp().notNull().defaultNow(),
- expiresAt: t.timestamp().notNull(),
- deviceId: t
- .integer()
- .references(() => core_sessions_known_devices.id, {
- onDelete: 'cascade',
- })
- .notNull(),
- }),
- t => [index('core_sessions_user_id_idx').on(t.userId)],
-).enableRLS();
-
-export const core_sessions_relations = relations(core_sessions, ({ one }) => ({
- user: one(core_users, {
- fields: [core_sessions.userId],
- references: [core_users.id],
- }),
- device: one(core_sessions_known_devices, {
- fields: [core_sessions.deviceId],
- references: [core_sessions_known_devices.id],
- }),
-}));
-
-export const core_sessions_known_devices = pgTable(
- 'core_sessions_known_devices',
- t => ({
- id: t.serial().primaryKey(),
- ipAddress: t.varchar({ length: 40 }).notNull(),
- userAgent: t.text().notNull(),
- lastSeen: t.timestamp().notNull().defaultNow(),
- }),
- t => [index('core_sessions_known_devices_ip_address_idx').on(t.ipAddress)],
-).enableRLS();
-
-export const core_sessions_known_devices_relations = relations(
- core_sessions_known_devices,
- ({ one }) => ({
- session: one(core_sessions, {
- fields: [core_sessions_known_devices.id],
- references: [core_sessions.deviceId],
- }),
- }),
-);
diff --git a/packages/vitnode/src/database/schema/test.ts b/packages/vitnode/src/database/schema/test.ts
deleted file mode 100644
index 0cbdbd526..000000000
--- a/packages/vitnode/src/database/schema/test.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { pgTable } from 'drizzle-orm/pg-core';
-
-export const core_test = pgTable('core_test', t => ({
- id: t.serial().primaryKey(),
- createdAt: t.timestamp().notNull().defaultNow(),
- text: t.text().notNull(),
-})).enableRLS();
diff --git a/packages/vitnode/src/database/schema/users.ts b/packages/vitnode/src/database/schema/users.ts
deleted file mode 100644
index 3c134fbf8..000000000
--- a/packages/vitnode/src/database/schema/users.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-import { relations } from 'drizzle-orm';
-import { index, pgTable } from 'drizzle-orm/pg-core';
-
-import { core_languages } from './languages';
-import { core_roles } from './roles';
-
-export const core_users = pgTable(
- 'core_users',
- t => ({
- id: t.serial().primaryKey(),
- nameCode: t.varchar({ length: 255 }).notNull().unique(),
- name: t.varchar({ length: 255 }).notNull().unique(),
- email: t.varchar({ length: 255 }).notNull().unique(),
- password: t.varchar(),
- createdAt: t.timestamp().notNull().defaultNow(),
- newsletter: t.boolean().notNull().default(false),
- avatarColor: t.varchar({ length: 6 }).notNull(),
- emailVerified: t.boolean().notNull().default(false),
- roleId: t
- .integer()
- .references(() => core_roles.id)
- .notNull(),
- birthday: t.timestamp(),
- ipAddress: t.varchar({ length: 40 }).notNull(),
- language: t
- .varchar({ length: 32 })
- .notNull()
- .default('en')
- .references(() => core_languages.code, {
- onDelete: 'set default',
- }),
- }),
- t => [
- index('core_users_name_code_idx').on(t.nameCode),
- index('core_users_name_idx').on(t.name),
- index('core_users_email_idx').on(t.email),
- ],
-).enableRLS();
-
-export const core_users_relations = relations(core_users, ({ one, many }) => ({
- group: one(core_roles, {
- fields: [core_users.roleId],
- references: [core_roles.id],
- }),
- language: one(core_languages, {
- fields: [core_users.language],
- references: [core_languages.code],
- }),
- confirm_email: one(core_users_confirm_emails, {
- fields: [core_users.id],
- references: [core_users_confirm_emails.userId],
- }),
- sso: many(core_users_sso),
- forgot_password: one(core_users_forgot_password, {
- fields: [core_users.id],
- references: [core_users_forgot_password.userId],
- }),
-}));
-
-export const core_users_sso = pgTable(
- 'core_users_sso',
- t => ({
- userId: t
- .integer()
- .references(() => core_users.id, {
- onDelete: 'cascade',
- })
- .notNull(),
- providerId: t.varchar({ length: 255 }).notNull(),
- providerAccountId: t.varchar({ length: 255 }).notNull(),
- createdAt: t.timestamp().notNull().defaultNow(),
- updatedAt: t
- .timestamp()
- .notNull()
- .$onUpdate(() => new Date()),
- }),
- t => [index('core_users_sso_user_id_idx').on(t.userId)],
-).enableRLS();
-
-export const core_users_sso_relations = relations(
- core_users_sso,
- ({ one }) => ({
- user: one(core_users, {
- fields: [core_users_sso.userId],
- references: [core_users.id],
- }),
- }),
-);
-
-export const core_users_confirm_emails = pgTable(
- 'core_users_confirm_emails',
- t => ({
- id: t.serial().primaryKey(),
- userId: t
- .integer()
- .references(() => core_users.id, {
- onDelete: 'cascade',
- })
- .notNull(),
- token: t.varchar({ length: 100 }).notNull().unique(),
- createdAt: t.timestamp().notNull().defaultNow(),
- expires: t.timestamp().notNull(),
- }),
-).enableRLS();
-
-export const core_users_confirm_emails_relations = relations(
- core_users_confirm_emails,
- ({ one }) => ({
- user: one(core_users, {
- fields: [core_users_confirm_emails.userId],
- references: [core_users.id],
- }),
- }),
-);
-
-export const core_users_forgot_password = pgTable(
- 'core_users_forgot_password',
- t => ({
- id: t.serial().primaryKey(),
- userId: t
- .integer()
- .references(() => core_users.id, {
- onDelete: 'cascade',
- })
- .notNull()
- .unique(),
- token: t.varchar({ length: 100 }).notNull().unique(),
- ip_address: t.varchar({ length: 40 }).notNull(),
- createdAt: t.timestamp().notNull().defaultNow(),
- expiresAt: t.timestamp().notNull(),
- }),
-).enableRLS();
-
-export const core_users_forgot_password_relations = relations(
- core_users_forgot_password,
- ({ one }) => ({
- user: one(core_users, {
- fields: [core_users_forgot_password.userId],
- references: [core_users.id],
- }),
- }),
-);
diff --git a/apps/web/src/database/schema/sessions.ts b/packages/vitnode/src/database/sessions.ts
similarity index 100%
rename from apps/web/src/database/schema/sessions.ts
rename to packages/vitnode/src/database/sessions.ts
diff --git a/apps/web/src/database/schema/test.ts b/packages/vitnode/src/database/test.ts
similarity index 100%
rename from apps/web/src/database/schema/test.ts
rename to packages/vitnode/src/database/test.ts
diff --git a/apps/web/src/database/schema/users.ts b/packages/vitnode/src/database/users.ts
similarity index 100%
rename from apps/web/src/database/schema/users.ts
rename to packages/vitnode/src/database/users.ts
diff --git a/packages/vitnode/src/drizzle.config.ts b/packages/vitnode/src/drizzle.config.ts
new file mode 100644
index 000000000..04521fe7e
--- /dev/null
+++ b/packages/vitnode/src/drizzle.config.ts
@@ -0,0 +1,39 @@
+import type { Config } from 'drizzle-kit';
+
+import { defineConfig } from 'drizzle-kit';
+import { join } from 'path';
+
+import type { VitNodeApiConfig } from './vitnode.config';
+
+export const defineVitNodeDrizzleConfig = ({
+ vitNodeApiConfig,
+ ...args
+}: Config & {
+ vitNodeApiConfig: VitNodeApiConfig;
+}) => {
+ const pluginNames = vitNodeApiConfig.plugins.map(plugin => plugin.name);
+
+ const pluginPaths = ['@vitnode/core', ...pluginNames].map(pluginName => {
+ const pluginPath = join(
+ process.cwd(),
+ 'node_modules',
+ pluginName,
+ 'src',
+ 'database',
+ );
+
+ return pluginPath;
+ });
+
+ return defineConfig({
+ ...args,
+ schema: [
+ ...(Array.isArray(args.schema)
+ ? args.schema
+ : args.schema
+ ? [args.schema]
+ : []),
+ ...pluginPaths,
+ ],
+ });
+};
diff --git a/packages/vitnode/src/vitnode.config.ts b/packages/vitnode/src/vitnode.config.ts
index 3269aa68a..d4ff34c3c 100644
--- a/packages/vitnode/src/vitnode.config.ts
+++ b/packages/vitnode/src/vitnode.config.ts
@@ -1,3 +1,4 @@
+import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import type { ThemeProvider } from 'next-themes';
import type { BuildPluginApiReturn } from './api/lib/plugin';
@@ -35,6 +36,7 @@ export interface VitNodeApiConfig {
deviceCookieName?: string;
ssoPlugins?: SSOApiPlugin[];
};
+ dbProvider: PostgresJsDatabase;
emailProvider?: EmailApiPlugin;
plugins: BuildPluginApiReturn[];
}