mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 20:26:42 +02:00
Merge branch 'dev-1' into dev
This commit is contained in:
commit
d41d060bb3
1770 changed files with 168028 additions and 31031 deletions
38
apps-archived/storage/apps/backend/src/db/connection.ts
Normal file
38
apps-archived/storage/apps/backend/src/db/connection.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import * as schema from './schema';
|
||||
|
||||
// Use require for postgres to avoid ESM/CommonJS interop issues
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const postgres = require('postgres');
|
||||
|
||||
let connection: ReturnType<typeof postgres> | null = null;
|
||||
let db: ReturnType<typeof drizzle> | null = null;
|
||||
|
||||
export function getConnection(databaseUrl: string) {
|
||||
if (!connection) {
|
||||
connection = postgres(databaseUrl, {
|
||||
max: 10,
|
||||
idle_timeout: 20,
|
||||
connect_timeout: 10,
|
||||
});
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
export function getDb(databaseUrl: string) {
|
||||
if (!db) {
|
||||
const conn = getConnection(databaseUrl);
|
||||
db = drizzle(conn, { schema });
|
||||
}
|
||||
return db;
|
||||
}
|
||||
|
||||
export async function closeConnection() {
|
||||
if (connection) {
|
||||
await connection.end();
|
||||
connection = null;
|
||||
db = null;
|
||||
}
|
||||
}
|
||||
|
||||
export type Database = ReturnType<typeof getDb>;
|
||||
30
apps-archived/storage/apps/backend/src/db/database.module.ts
Normal file
30
apps-archived/storage/apps/backend/src/db/database.module.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Module, Global } from '@nestjs/common';
|
||||
import type { OnModuleDestroy } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { getDb, closeConnection } from './connection';
|
||||
import type { Database } from './connection';
|
||||
|
||||
export const DATABASE_CONNECTION = 'DATABASE_CONNECTION';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: DATABASE_CONNECTION,
|
||||
useFactory: (configService: ConfigService): Database => {
|
||||
const databaseUrl = configService.get<string>('DATABASE_URL');
|
||||
if (!databaseUrl) {
|
||||
throw new Error('DATABASE_URL environment variable is not set');
|
||||
}
|
||||
return getDb(databaseUrl);
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
],
|
||||
exports: [DATABASE_CONNECTION],
|
||||
})
|
||||
export class DatabaseModule implements OnModuleDestroy {
|
||||
async onModuleDestroy() {
|
||||
await closeConnection();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { pgTable, uuid, primaryKey } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import { files } from './files.schema';
|
||||
import { tags } from './tags.schema';
|
||||
|
||||
export const fileTags = pgTable(
|
||||
'file_tags',
|
||||
{
|
||||
fileId: uuid('file_id')
|
||||
.references(() => files.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
tagId: uuid('tag_id')
|
||||
.references(() => tags.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
},
|
||||
(table) => ({
|
||||
pk: primaryKey({ columns: [table.fileId, table.tagId] }),
|
||||
})
|
||||
);
|
||||
|
||||
export const fileTagsRelations = relations(fileTags, ({ one }) => ({
|
||||
file: one(files, {
|
||||
fields: [fileTags.fileId],
|
||||
references: [files.id],
|
||||
}),
|
||||
tag: one(tags, {
|
||||
fields: [fileTags.tagId],
|
||||
references: [tags.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type FileTag = typeof fileTags.$inferSelect;
|
||||
export type NewFileTag = typeof fileTags.$inferInsert;
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { pgTable, uuid, varchar, timestamp, bigint, integer, text } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import { files } from './files.schema';
|
||||
|
||||
export const fileVersions = pgTable('file_versions', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
fileId: uuid('file_id')
|
||||
.references(() => files.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
|
||||
// Version info
|
||||
versionNumber: integer('version_number').notNull(),
|
||||
|
||||
// Storage info for this version
|
||||
storagePath: varchar('storage_path', { length: 1000 }).notNull(),
|
||||
storageKey: varchar('storage_key', { length: 500 }).notNull(),
|
||||
size: bigint('size', { mode: 'number' }).notNull(),
|
||||
checksum: varchar('checksum', { length: 64 }),
|
||||
|
||||
// Metadata
|
||||
comment: text('comment'), // Optional version comment
|
||||
createdBy: varchar('created_by', { length: 255 }).notNull(),
|
||||
|
||||
// Timestamps
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const fileVersionsRelations = relations(fileVersions, ({ one }) => ({
|
||||
file: one(files, {
|
||||
fields: [fileVersions.fileId],
|
||||
references: [files.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type FileVersion = typeof fileVersions.$inferSelect;
|
||||
export type NewFileVersion = typeof fileVersions.$inferInsert;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
varchar,
|
||||
text,
|
||||
timestamp,
|
||||
bigint,
|
||||
boolean,
|
||||
integer,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import { folders } from './folders.schema';
|
||||
|
||||
export const files = pgTable('files', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: varchar('user_id', { length: 255 }).notNull(),
|
||||
|
||||
// File metadata
|
||||
name: varchar('name', { length: 500 }).notNull(),
|
||||
originalName: varchar('original_name', { length: 500 }).notNull(),
|
||||
mimeType: varchar('mime_type', { length: 255 }).notNull(),
|
||||
size: bigint('size', { mode: 'number' }).notNull(),
|
||||
|
||||
// Storage location
|
||||
storagePath: varchar('storage_path', { length: 1000 }).notNull(),
|
||||
storageKey: varchar('storage_key', { length: 500 }).notNull().unique(),
|
||||
|
||||
// Hierarchy
|
||||
parentFolderId: uuid('parent_folder_id').references(() => folders.id, { onDelete: 'set null' }),
|
||||
|
||||
// File properties
|
||||
checksum: varchar('checksum', { length: 64 }), // SHA-256
|
||||
thumbnailPath: varchar('thumbnail_path', { length: 500 }),
|
||||
|
||||
// Versioning
|
||||
currentVersion: integer('current_version').default(1).notNull(),
|
||||
|
||||
// Status flags
|
||||
isFavorite: boolean('is_favorite').default(false).notNull(),
|
||||
isDeleted: boolean('is_deleted').default(false).notNull(),
|
||||
deletedAt: timestamp('deleted_at', { withTimezone: true }),
|
||||
|
||||
// Timestamps
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const filesRelations = relations(files, ({ one }) => ({
|
||||
parentFolder: one(folders, {
|
||||
fields: [files.parentFolderId],
|
||||
references: [folders.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type File = typeof files.$inferSelect;
|
||||
export type NewFile = typeof files.$inferInsert;
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { pgTable, uuid, varchar, timestamp, boolean, text, integer } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
export const folders = pgTable('folders', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: varchar('user_id', { length: 255 }).notNull(),
|
||||
|
||||
// Folder metadata
|
||||
name: varchar('name', { length: 255 }).notNull(),
|
||||
color: varchar('color', { length: 20 }),
|
||||
description: text('description'),
|
||||
|
||||
// Hierarchy (self-referencing)
|
||||
parentFolderId: uuid('parent_folder_id'),
|
||||
|
||||
// Path for efficient queries (e.g., "/root-uuid/parent-uuid/current-uuid")
|
||||
path: text('path').notNull(),
|
||||
depth: integer('depth').default(0).notNull(),
|
||||
|
||||
// Status flags
|
||||
isFavorite: boolean('is_favorite').default(false).notNull(),
|
||||
isDeleted: boolean('is_deleted').default(false).notNull(),
|
||||
deletedAt: timestamp('deleted_at', { withTimezone: true }),
|
||||
|
||||
// Timestamps
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// Self-referencing relation
|
||||
export const foldersRelations = relations(folders, ({ one, many }) => ({
|
||||
parentFolder: one(folders, {
|
||||
fields: [folders.parentFolderId],
|
||||
references: [folders.id],
|
||||
relationName: 'folder_parent',
|
||||
}),
|
||||
childFolders: many(folders, { relationName: 'folder_parent' }),
|
||||
}));
|
||||
|
||||
export type Folder = typeof folders.$inferSelect;
|
||||
export type NewFolder = typeof folders.$inferInsert;
|
||||
17
apps-archived/storage/apps/backend/src/db/schema/index.ts
Normal file
17
apps-archived/storage/apps/backend/src/db/schema/index.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Folders (must be first due to self-reference)
|
||||
export * from './folders.schema';
|
||||
|
||||
// Files (references folders)
|
||||
export * from './files.schema';
|
||||
|
||||
// File versions (references files)
|
||||
export * from './file-versions.schema';
|
||||
|
||||
// Tags
|
||||
export * from './tags.schema';
|
||||
|
||||
// File-Tags junction (references files and tags)
|
||||
export * from './file-tags.schema';
|
||||
|
||||
// Shares (references files and folders)
|
||||
export * from './shares.schema';
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { pgTable, uuid, varchar, timestamp, boolean, integer, pgEnum } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import { files } from './files.schema';
|
||||
import { folders } from './folders.schema';
|
||||
|
||||
export const shareTypeEnum = pgEnum('share_type', ['file', 'folder']);
|
||||
export const shareAccessEnum = pgEnum('share_access', ['view', 'edit', 'download']);
|
||||
|
||||
export const shares = pgTable('shares', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: varchar('user_id', { length: 255 }).notNull(), // Owner
|
||||
|
||||
// Share target (one of these will be set)
|
||||
fileId: uuid('file_id').references(() => files.id, { onDelete: 'cascade' }),
|
||||
folderId: uuid('folder_id').references(() => folders.id, { onDelete: 'cascade' }),
|
||||
shareType: shareTypeEnum('share_type').notNull(),
|
||||
|
||||
// Share link
|
||||
shareToken: varchar('share_token', { length: 64 }).notNull().unique(),
|
||||
accessLevel: shareAccessEnum('access_level').default('view').notNull(),
|
||||
|
||||
// Security
|
||||
password: varchar('password', { length: 255 }), // Hashed password
|
||||
maxDownloads: integer('max_downloads'),
|
||||
downloadCount: integer('download_count').default(0).notNull(),
|
||||
|
||||
// Expiration
|
||||
expiresAt: timestamp('expires_at', { withTimezone: true }),
|
||||
|
||||
// Status
|
||||
isActive: boolean('is_active').default(true).notNull(),
|
||||
|
||||
// Timestamps
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
lastAccessedAt: timestamp('last_accessed_at', { withTimezone: true }),
|
||||
});
|
||||
|
||||
export const sharesRelations = relations(shares, ({ one }) => ({
|
||||
file: one(files, {
|
||||
fields: [shares.fileId],
|
||||
references: [files.id],
|
||||
}),
|
||||
folder: one(folders, {
|
||||
fields: [shares.folderId],
|
||||
references: [folders.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export type Share = typeof shares.$inferSelect;
|
||||
export type NewShare = typeof shares.$inferInsert;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { pgTable, uuid, varchar, timestamp } from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
import { fileTags } from './file-tags.schema';
|
||||
|
||||
export const tags = pgTable('tags', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
userId: varchar('user_id', { length: 255 }).notNull(),
|
||||
|
||||
name: varchar('name', { length: 50 }).notNull(),
|
||||
color: varchar('color', { length: 20 }),
|
||||
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export const tagsRelations = relations(tags, ({ many }) => ({
|
||||
fileTags: many(fileTags),
|
||||
}));
|
||||
|
||||
export type Tag = typeof tags.$inferSelect;
|
||||
export type NewTag = typeof tags.$inferInsert;
|
||||
Loading…
Add table
Add a link
Reference in a new issue