mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
Feat: New project chat, uload refactor (postgress), hosting plans, uload landingpage
This commit is contained in:
parent
559eb08d8c
commit
fcf3a344b1
123 changed files with 7106 additions and 3715 deletions
|
|
@ -51,6 +51,18 @@ export const APP_BRANDING: Record<AppId, AppBranding> = {
|
|||
logoStroke: true,
|
||||
logoStrokeWidth: 1.5,
|
||||
},
|
||||
uload: {
|
||||
id: 'uload',
|
||||
name: 'uLoad',
|
||||
tagline: 'Smart URL Shortener',
|
||||
primaryColor: '#3b82f6',
|
||||
secondaryColor: '#60a5fa',
|
||||
// Link/Chain icon
|
||||
logoPath: 'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1',
|
||||
logoViewBox: '0 0 24 24',
|
||||
logoStroke: true,
|
||||
logoStrokeWidth: 2,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ export {
|
|||
MemoroLogo,
|
||||
ManaCoreLogo,
|
||||
ManaDeckLogo,
|
||||
StorytellerLogo
|
||||
StorytellerLogo,
|
||||
UloadLogo
|
||||
} from './logos';
|
||||
|
||||
// Configuration
|
||||
|
|
|
|||
13
packages/shared-branding/src/logos/UloadLogo.svelte
Normal file
13
packages/shared-branding/src/logos/UloadLogo.svelte
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<script lang="ts">
|
||||
import AppLogo from '../AppLogo.svelte';
|
||||
|
||||
interface Props {
|
||||
size?: number;
|
||||
color?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let { size = 55, color, class: className = '' }: Props = $props();
|
||||
</script>
|
||||
|
||||
<AppLogo app="uload" {size} {color} class={className} />
|
||||
|
|
@ -6,3 +6,4 @@ export { default as MemoroLogo } from './MemoroLogo.svelte';
|
|||
export { default as ManaCoreLogo } from './ManaCoreLogo.svelte';
|
||||
export { default as ManaDeckLogo } from './ManaDeckLogo.svelte';
|
||||
export { default as StorytellerLogo } from './StorytellerLogo.svelte';
|
||||
export { default as UloadLogo } from './UloadLogo.svelte';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* App identifiers for branding
|
||||
*/
|
||||
export type AppId = 'memoro' | 'manacore' | 'manadeck' | 'maerchenzauber';
|
||||
export type AppId = 'memoro' | 'manacore' | 'manadeck' | 'maerchenzauber' | 'uload';
|
||||
|
||||
/**
|
||||
* App branding configuration
|
||||
|
|
|
|||
4
packages/uload-database/.env.example
Normal file
4
packages/uload-database/.env.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Database connection string
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5434/uload
|
||||
# Or use project-specific variable
|
||||
ULOAD_DATABASE_URL=postgresql://postgres:postgres@localhost:5434/uload
|
||||
36
packages/uload-database/docker-compose.yml
Normal file
36
packages/uload-database/docker-compose.yml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: uload-postgres
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '5434:5432'
|
||||
environment:
|
||||
POSTGRES_DB: uload
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- uload_postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:latest
|
||||
container_name: uload-pgadmin
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '5051:80'
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: admin@uload.local
|
||||
PGADMIN_DEFAULT_PASSWORD: admin
|
||||
volumes:
|
||||
- uload_pgadmin_data:/var/lib/pgadmin
|
||||
depends_on:
|
||||
- postgres
|
||||
|
||||
volumes:
|
||||
uload_postgres_data:
|
||||
uload_pgadmin_data:
|
||||
12
packages/uload-database/drizzle.config.ts
Normal file
12
packages/uload-database/drizzle.config.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
export default defineConfig({
|
||||
schema: './src/schema/index.ts',
|
||||
out: './drizzle',
|
||||
dialect: 'postgresql',
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL || process.env.ULOAD_DATABASE_URL || '',
|
||||
},
|
||||
verbose: true,
|
||||
strict: true,
|
||||
});
|
||||
54
packages/uload-database/package.json
Normal file
54
packages/uload-database/package.json
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "@manacore/uload-database",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./schema": {
|
||||
"types": "./dist/schema/index.d.ts",
|
||||
"import": "./dist/schema/index.js",
|
||||
"require": "./dist/schema/index.js",
|
||||
"default": "./dist/schema/index.js"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./dist/client.d.ts",
|
||||
"import": "./dist/client.js",
|
||||
"require": "./dist/client.js",
|
||||
"default": "./dist/client.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"prepare": "pnpm build",
|
||||
"docker:up": "docker compose up -d",
|
||||
"docker:down": "docker compose down",
|
||||
"docker:logs": "docker compose logs -f postgres",
|
||||
"db:generate": "dotenv -- drizzle-kit generate",
|
||||
"db:migrate": "dotenv -- drizzle-kit migrate",
|
||||
"db:push": "dotenv -- drizzle-kit push --force",
|
||||
"db:studio": "dotenv -- drizzle-kit studio",
|
||||
"db:reset": "docker compose down -v && docker compose up -d && sleep 3 && pnpm db:push",
|
||||
"db:test": "dotenv -- tsx src/test-connection.ts",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"drizzle-orm": "^0.36.0",
|
||||
"postgres": "^3.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv-cli": "^7.4.0",
|
||||
"drizzle-kit": "^0.28.0",
|
||||
"tsx": "^4.19.0",
|
||||
"typescript": "^5.7.3",
|
||||
"@types/node": "^22.10.0"
|
||||
}
|
||||
}
|
||||
97
packages/uload-database/src/client.ts
Normal file
97
packages/uload-database/src/client.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import postgres from 'postgres';
|
||||
import * as schema from './schema/index.js';
|
||||
|
||||
// Singleton instance for the database client
|
||||
let dbInstance: ReturnType<typeof drizzle<typeof schema>> | null = null;
|
||||
let pgClient: ReturnType<typeof postgres> | null = null;
|
||||
|
||||
/**
|
||||
* Get the database URL from environment variables
|
||||
*/
|
||||
function getDatabaseUrl(): string {
|
||||
const url = process.env.DATABASE_URL || process.env.ULOAD_DATABASE_URL;
|
||||
if (!url) {
|
||||
throw new Error(
|
||||
'Database URL not found. Set DATABASE_URL or ULOAD_DATABASE_URL environment variable.'
|
||||
);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new database client
|
||||
* Uses connection pooling with sensible defaults for serverless environments
|
||||
*/
|
||||
export function createClient(connectionString?: string) {
|
||||
const url = connectionString || getDatabaseUrl();
|
||||
|
||||
const client = postgres(url, {
|
||||
max: 10, // Maximum connections in the pool
|
||||
idle_timeout: 20, // Close idle connections after 20 seconds
|
||||
connect_timeout: 10, // Connection timeout in seconds
|
||||
prepare: false, // Disable prepared statements for serverless
|
||||
});
|
||||
|
||||
return drizzle(client, { schema });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton database instance
|
||||
* Creates a new instance if one doesn't exist
|
||||
*/
|
||||
export function getDb() {
|
||||
if (!dbInstance) {
|
||||
const url = getDatabaseUrl();
|
||||
pgClient = postgres(url, {
|
||||
max: 10,
|
||||
idle_timeout: 20,
|
||||
connect_timeout: 10,
|
||||
prepare: false,
|
||||
});
|
||||
dbInstance = drizzle(pgClient, { schema });
|
||||
}
|
||||
return dbInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the database connection
|
||||
* Should be called when shutting down the application
|
||||
*/
|
||||
export async function closeDb() {
|
||||
if (pgClient) {
|
||||
await pgClient.end();
|
||||
pgClient = null;
|
||||
dbInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Export the database type for typing purposes
|
||||
export type Database = ReturnType<typeof createClient>;
|
||||
|
||||
// Re-export commonly used Drizzle utilities
|
||||
export {
|
||||
eq,
|
||||
ne,
|
||||
gt,
|
||||
gte,
|
||||
lt,
|
||||
lte,
|
||||
and,
|
||||
or,
|
||||
not,
|
||||
inArray,
|
||||
notInArray,
|
||||
isNull,
|
||||
isNotNull,
|
||||
like,
|
||||
ilike,
|
||||
sql,
|
||||
asc,
|
||||
desc,
|
||||
count,
|
||||
sum,
|
||||
avg,
|
||||
min,
|
||||
max,
|
||||
} from 'drizzle-orm';
|
||||
32
packages/uload-database/src/index.ts
Normal file
32
packages/uload-database/src/index.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Database client exports
|
||||
export { createClient, getDb, closeDb, type Database } from './client.js';
|
||||
|
||||
// Re-export Drizzle utilities
|
||||
export {
|
||||
eq,
|
||||
ne,
|
||||
gt,
|
||||
gte,
|
||||
lt,
|
||||
lte,
|
||||
and,
|
||||
or,
|
||||
not,
|
||||
inArray,
|
||||
notInArray,
|
||||
isNull,
|
||||
isNotNull,
|
||||
like,
|
||||
ilike,
|
||||
sql,
|
||||
asc,
|
||||
desc,
|
||||
count,
|
||||
sum,
|
||||
avg,
|
||||
min,
|
||||
max,
|
||||
} from './client.js';
|
||||
|
||||
// Schema exports
|
||||
export * from './schema/index.js';
|
||||
32
packages/uload-database/src/schema/accounts.ts
Normal file
32
packages/uload-database/src/schema/accounts.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
boolean,
|
||||
timestamp,
|
||||
jsonb,
|
||||
index,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { users } from './users.js';
|
||||
|
||||
export const accounts = pgTable(
|
||||
'accounts',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
name: text('name').notNull(),
|
||||
owner: uuid('owner')
|
||||
.references(() => users.id)
|
||||
.notNull(),
|
||||
isActive: boolean('is_active').default(true),
|
||||
planType: text('plan_type', { enum: ['free', 'team', 'enterprise'] }).default(
|
||||
'free'
|
||||
),
|
||||
settings: jsonb('settings'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [index('accounts_owner_idx').on(table.owner)]
|
||||
);
|
||||
|
||||
export type Account = typeof accounts.$inferSelect;
|
||||
export type NewAccount = typeof accounts.$inferInsert;
|
||||
33
packages/uload-database/src/schema/clicks.ts
Normal file
33
packages/uload-database/src/schema/clicks.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { pgTable, uuid, text, timestamp, index } from 'drizzle-orm/pg-core';
|
||||
import { links } from './links.js';
|
||||
|
||||
export const clicks = pgTable(
|
||||
'clicks',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
linkId: uuid('link_id')
|
||||
.references(() => links.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
ipHash: text('ip_hash'),
|
||||
userAgent: text('user_agent'),
|
||||
referer: text('referer'),
|
||||
browser: text('browser'),
|
||||
deviceType: text('device_type'),
|
||||
os: text('os'),
|
||||
country: text('country'),
|
||||
city: text('city'),
|
||||
clickedAt: timestamp('clicked_at').defaultNow().notNull(),
|
||||
utmSource: text('utm_source'),
|
||||
utmMedium: text('utm_medium'),
|
||||
utmCampaign: text('utm_campaign'),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('clicks_link_id_idx').on(table.linkId),
|
||||
index('clicks_clicked_at_idx').on(table.clickedAt),
|
||||
index('clicks_country_idx').on(table.country),
|
||||
]
|
||||
);
|
||||
|
||||
export type Click = typeof clicks.$inferSelect;
|
||||
export type NewClick = typeof clicks.$inferInsert;
|
||||
18
packages/uload-database/src/schema/index.ts
Normal file
18
packages/uload-database/src/schema/index.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Tables
|
||||
export { users, type User, type NewUser } from './users.js';
|
||||
export { accounts, type Account, type NewAccount } from './accounts.js';
|
||||
export { workspaces, type Workspace, type NewWorkspace } from './workspaces.js';
|
||||
export { links, type Link, type NewLink } from './links.js';
|
||||
export { clicks, type Click, type NewClick } from './clicks.js';
|
||||
export { tags, linkTags, type Tag, type NewTag, type LinkTag, type NewLinkTag } from './tags.js';
|
||||
|
||||
// Relations
|
||||
export {
|
||||
usersRelations,
|
||||
linksRelations,
|
||||
clicksRelations,
|
||||
tagsRelations,
|
||||
linkTagsRelations,
|
||||
accountsRelations,
|
||||
workspacesRelations,
|
||||
} from './relations.js';
|
||||
50
packages/uload-database/src/schema/links.ts
Normal file
50
packages/uload-database/src/schema/links.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
boolean,
|
||||
integer,
|
||||
timestamp,
|
||||
jsonb,
|
||||
index,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { users } from './users.js';
|
||||
import { accounts } from './accounts.js';
|
||||
import { workspaces } from './workspaces.js';
|
||||
|
||||
export const links = pgTable(
|
||||
'links',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
shortCode: text('short_code').unique().notNull(),
|
||||
customCode: text('custom_code'),
|
||||
originalUrl: text('original_url').notNull(),
|
||||
title: text('title'),
|
||||
description: text('description'),
|
||||
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
||||
isActive: boolean('is_active').default(true),
|
||||
password: text('password'), // hashed
|
||||
maxClicks: integer('max_clicks'),
|
||||
expiresAt: timestamp('expires_at'),
|
||||
clickCount: integer('click_count').default(0),
|
||||
qrCodeUrl: text('qr_code_url'),
|
||||
tags: jsonb('tags').$type<string[]>(),
|
||||
utmSource: text('utm_source'),
|
||||
utmMedium: text('utm_medium'),
|
||||
utmCampaign: text('utm_campaign'),
|
||||
accountOwner: uuid('account_owner').references(() => accounts.id),
|
||||
workspaceId: uuid('workspace_id').references(() => workspaces.id),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('links_user_id_idx').on(table.userId),
|
||||
index('links_short_code_idx').on(table.shortCode),
|
||||
index('links_workspace_id_idx').on(table.workspaceId),
|
||||
index('links_account_owner_idx').on(table.accountOwner),
|
||||
index('links_is_active_idx').on(table.isActive),
|
||||
]
|
||||
);
|
||||
|
||||
export type Link = typeof links.$inferSelect;
|
||||
export type NewLink = typeof links.$inferInsert;
|
||||
52
packages/uload-database/src/schema/relations.ts
Normal file
52
packages/uload-database/src/schema/relations.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { relations } from 'drizzle-orm';
|
||||
import { users } from './users.js';
|
||||
import { links } from './links.js';
|
||||
import { clicks } from './clicks.js';
|
||||
import { tags, linkTags } from './tags.js';
|
||||
import { accounts } from './accounts.js';
|
||||
import { workspaces } from './workspaces.js';
|
||||
|
||||
export const usersRelations = relations(users, ({ many }) => ({
|
||||
links: many(links),
|
||||
tags: many(tags),
|
||||
ownedAccounts: many(accounts),
|
||||
ownedWorkspaces: many(workspaces),
|
||||
}));
|
||||
|
||||
export const linksRelations = relations(links, ({ one, many }) => ({
|
||||
user: one(users, { fields: [links.userId], references: [users.id] }),
|
||||
account: one(accounts, {
|
||||
fields: [links.accountOwner],
|
||||
references: [accounts.id],
|
||||
}),
|
||||
workspace: one(workspaces, {
|
||||
fields: [links.workspaceId],
|
||||
references: [workspaces.id],
|
||||
}),
|
||||
clicks: many(clicks),
|
||||
linkTags: many(linkTags),
|
||||
}));
|
||||
|
||||
export const clicksRelations = relations(clicks, ({ one }) => ({
|
||||
link: one(links, { fields: [clicks.linkId], references: [links.id] }),
|
||||
}));
|
||||
|
||||
export const tagsRelations = relations(tags, ({ one, many }) => ({
|
||||
user: one(users, { fields: [tags.userId], references: [users.id] }),
|
||||
linkTags: many(linkTags),
|
||||
}));
|
||||
|
||||
export const linkTagsRelations = relations(linkTags, ({ one }) => ({
|
||||
link: one(links, { fields: [linkTags.linkId], references: [links.id] }),
|
||||
tag: one(tags, { fields: [linkTags.tagId], references: [tags.id] }),
|
||||
}));
|
||||
|
||||
export const accountsRelations = relations(accounts, ({ one, many }) => ({
|
||||
owner: one(users, { fields: [accounts.owner], references: [users.id] }),
|
||||
links: many(links),
|
||||
}));
|
||||
|
||||
export const workspacesRelations = relations(workspaces, ({ one, many }) => ({
|
||||
owner: one(users, { fields: [workspaces.owner], references: [users.id] }),
|
||||
links: many(links),
|
||||
}));
|
||||
56
packages/uload-database/src/schema/tags.ts
Normal file
56
packages/uload-database/src/schema/tags.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
boolean,
|
||||
integer,
|
||||
timestamp,
|
||||
index,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { users } from './users.js';
|
||||
import { links } from './links.js';
|
||||
|
||||
export const tags = pgTable(
|
||||
'tags',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
name: text('name').notNull(),
|
||||
slug: text('slug').notNull(),
|
||||
color: text('color'),
|
||||
icon: text('icon'),
|
||||
isPublic: boolean('is_public').default(false),
|
||||
usageCount: integer('usage_count').default(0),
|
||||
userId: uuid('user_id').references(() => users.id),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('tags_user_id_idx').on(table.userId),
|
||||
index('tags_slug_idx').on(table.slug),
|
||||
]
|
||||
);
|
||||
|
||||
export type Tag = typeof tags.$inferSelect;
|
||||
export type NewTag = typeof tags.$inferInsert;
|
||||
|
||||
export const linkTags = pgTable(
|
||||
'link_tags',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
linkId: uuid('link_id')
|
||||
.references(() => links.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
tagId: uuid('tag_id')
|
||||
.references(() => tags.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('link_tags_link_id_idx').on(table.linkId),
|
||||
index('link_tags_tag_id_idx').on(table.tagId),
|
||||
index('link_tags_unique_idx').on(table.linkId, table.tagId),
|
||||
]
|
||||
);
|
||||
|
||||
export type LinkTag = typeof linkTags.$inferSelect;
|
||||
export type NewLinkTag = typeof linkTags.$inferInsert;
|
||||
47
packages/uload-database/src/schema/users.ts
Normal file
47
packages/uload-database/src/schema/users.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import {
|
||||
pgTable,
|
||||
uuid,
|
||||
text,
|
||||
boolean,
|
||||
integer,
|
||||
timestamp,
|
||||
index,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
export const users = pgTable(
|
||||
'users',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
externalAuthId: text('external_auth_id').unique(), // For Mana Core auth
|
||||
email: text('email').unique().notNull(),
|
||||
username: text('username').unique().notNull(),
|
||||
name: text('name'),
|
||||
avatarUrl: text('avatar_url'),
|
||||
bio: text('bio'),
|
||||
location: text('location'),
|
||||
website: text('website'),
|
||||
github: text('github'),
|
||||
twitter: text('twitter'),
|
||||
linkedin: text('linkedin'),
|
||||
instagram: text('instagram'),
|
||||
publicProfile: boolean('public_profile').default(false),
|
||||
showClickStats: boolean('show_click_stats').default(true),
|
||||
emailNotifications: boolean('email_notifications').default(true),
|
||||
defaultExpiry: integer('default_expiry'),
|
||||
profileBackground: text('profile_background'),
|
||||
verified: boolean('verified').default(false),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('users_email_idx').on(table.email),
|
||||
index('users_username_idx').on(table.username),
|
||||
index('users_external_auth_id_idx').on(table.externalAuthId),
|
||||
]
|
||||
);
|
||||
|
||||
export type User = typeof users.$inferSelect;
|
||||
export type NewUser = typeof users.$inferInsert;
|
||||
|
||||
// Relations will be defined in relations.ts to avoid circular imports
|
||||
24
packages/uload-database/src/schema/workspaces.ts
Normal file
24
packages/uload-database/src/schema/workspaces.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { pgTable, uuid, text, timestamp, index } from 'drizzle-orm/pg-core';
|
||||
import { users } from './users.js';
|
||||
|
||||
export const workspaces = pgTable(
|
||||
'workspaces',
|
||||
{
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
name: text('name').notNull(),
|
||||
slug: text('slug').unique().notNull(),
|
||||
type: text('type', { enum: ['personal', 'team'] }).notNull(),
|
||||
owner: uuid('owner')
|
||||
.references(() => users.id)
|
||||
.notNull(),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||
},
|
||||
(table) => [
|
||||
index('workspaces_slug_idx').on(table.slug),
|
||||
index('workspaces_owner_idx').on(table.owner),
|
||||
]
|
||||
);
|
||||
|
||||
export type Workspace = typeof workspaces.$inferSelect;
|
||||
export type NewWorkspace = typeof workspaces.$inferInsert;
|
||||
21
packages/uload-database/tsconfig.json
Normal file
21
packages/uload-database/tsconfig.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["ES2022"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue