diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ddb0c99
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,172 @@
+.PHONY: help install dev build clean db-up db-down db-restart db-clean db-migrate db-generate db-push db-studio docker-down docker-clean dev-client dev-server dev-landing dev-all clean-deps clean-build clean-all format lint check-types
+
+# Default target - show help
+help:
+ @echo "MemoBrain - Available Make Commands"
+ @echo ""
+ @echo "π¦ Installation & Setup:"
+ @echo " make install - Install all dependencies"
+ @echo ""
+ @echo "ποΈ Database Commands:"
+ @echo " make db-up - Start database container"
+ @echo " make db-down - Stop database container"
+ @echo " make db-restart - Restart database container"
+ @echo " make db-clean - Stop and remove database container & volumes"
+ @echo " make db-generate - Generate Drizzle migrations"
+ @echo " make db-push - Push schema changes to database"
+ @echo " make db-migrate - Run database migrations"
+ @echo " make db-studio - Open Drizzle Studio"
+ @echo ""
+ @echo "π³ Docker Commands:"
+ @echo " make docker-down - Stop all Docker containers"
+ @echo " make docker-clean - Remove all containers, volumes, and images"
+ @echo ""
+ @echo "π Development Commands:"
+ @echo " make dev - Run all apps in development mode (turbo)"
+ @echo " make dev-client - Run client app only"
+ @echo " make dev-server - Run server app only"
+ @echo " make dev-landing - Run landing app only"
+ @echo " make dev-all - Run all apps individually (parallel)"
+ @echo ""
+ @echo "ποΈ Build Commands:"
+ @echo " make build - Build all apps"
+ @echo " make build-client - Build client app only"
+ @echo " make build-server - Build server app only"
+ @echo " make build-landing - Build landing app only"
+ @echo ""
+ @echo "π§Ή Cleanup Commands:"
+ @echo " make clean-deps - Remove all node_modules"
+ @echo " make clean-build - Remove all build artifacts"
+ @echo " make clean-all - Remove all generated files (deps + builds)"
+ @echo " make clean - Alias for clean-all"
+ @echo ""
+ @echo "π§ Utility Commands:"
+ @echo " make format - Format code with Prettier"
+ @echo " make lint - Lint all packages"
+ @echo " make check-types - Type-check all packages"
+ @echo ""
+
+# Installation
+install:
+ @echo "π¦ Installing dependencies..."
+ bun install
+
+# Database Commands
+db-up:
+ @echo "ποΈ Starting database..."
+ cd packages/db && docker compose up -d
+
+db-down:
+ @echo "π Stopping database..."
+ cd packages/db && docker compose down
+
+db-restart:
+ @echo "π Restarting database..."
+ cd packages/db && docker compose restart
+
+db-clean:
+ @echo "π§Ή Cleaning database (removing containers and volumes)..."
+ cd packages/db && docker compose down -v
+
+db-generate:
+ @echo "π¨ Generating Drizzle migrations..."
+ cd packages/db && bun run drizzle:generate
+
+db-push:
+ @echo "β¬οΈ Pushing schema to database..."
+ cd packages/db && bun run drizzle:push
+
+db-migrate:
+ @echo "π Running database migrations..."
+ cd packages/db && bun run drizzle:migrate
+
+db-studio:
+ @echo "π¨ Opening Drizzle Studio..."
+ cd packages/db && bun run drizzle:studio
+
+# Docker Commands
+docker-down:
+ @echo "π³ Stopping all Docker containers..."
+ docker compose down
+
+docker-clean:
+ @echo "π§Ή Cleaning all Docker resources..."
+ docker compose down -v --rmi all --remove-orphans
+
+# Development Commands
+dev:
+ @echo "π Starting all apps in development mode..."
+ bun run dev
+
+dev-client:
+ @echo "π Starting client app..."
+ cd apps/client && bun run dev
+
+dev-server:
+ @echo "π Starting server app..."
+ cd apps/server && bun run dev
+
+dev-landing:
+ @echo "π Starting landing app..."
+ cd apps/landing && bun run dev
+
+dev-all:
+ @echo "π Starting all apps individually..."
+ @echo "Note: Run each in a separate terminal or use a process manager"
+ @echo "Terminal 1: make dev-client"
+ @echo "Terminal 2: make dev-server"
+ @echo "Terminal 3: make dev-landing"
+
+# Build Commands
+build:
+ @echo "ποΈ Building all apps..."
+ bun run build
+
+build-client:
+ @echo "ποΈ Building client app..."
+ cd apps/client && bun run build
+
+build-server:
+ @echo "ποΈ Building server app..."
+ cd apps/server && bun run build
+
+build-landing:
+ @echo "ποΈ Building landing app..."
+ cd apps/landing && bun run build
+
+# Cleanup Commands
+clean-deps:
+ @echo "π§Ή Removing all node_modules..."
+ rm -rf node_modules
+ rm -rf apps/*/node_modules
+ rm -rf packages/*/node_modules
+ @echo "β
Dependencies cleaned!"
+
+clean-build:
+ @echo "π§Ή Removing build artifacts..."
+ rm -rf apps/client/dist
+ rm -rf apps/landing/.next
+ rm -rf apps/server/dist
+ rm -rf packages/*/dist
+ rm -rf .turbo
+ @echo "β
Build artifacts cleaned!"
+
+clean-all: clean-build clean-deps
+ @echo "π§Ή Removing all generated files..."
+ rm -rf bun.lockb
+ @echo "β
All cleaned! Run 'make install' to reinstall."
+
+clean: clean-all
+
+# Utility Commands
+format:
+ @echo "β¨ Formatting code..."
+ bun run format
+
+lint:
+ @echo "π Linting code..."
+ bun run lint
+
+check-types:
+ @echo "π Type-checking..."
+ bun run check-types
\ No newline at end of file
diff --git a/apps/client/package.json b/apps/client/package.json
index c7feea0..2fa47d2 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -11,6 +11,7 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
+ "@assets": "workspace:*",
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
diff --git a/apps/client/src/index.css b/apps/client/src/index.css
index da7f15a..5e31d6c 100644
--- a/apps/client/src/index.css
+++ b/apps/client/src/index.css
@@ -1,70 +1,22 @@
+@import "tailwindcss";
@import "@repo/tailwind-config/theme.css";
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
-}
-
body {
margin: 0;
- display: flex;
- place-items: center;
min-width: 320px;
min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
+ display: flex;
+ place-items: center;
+ /* Use shared Tailwind color tokens from packages/tailwind-config/theme.css */
+ @apply bg-background text-text;
}
diff --git a/apps/client/vite.config.ts b/apps/client/vite.config.ts
index 8b0f57b..4bd471b 100644
--- a/apps/client/vite.config.ts
+++ b/apps/client/vite.config.ts
@@ -1,7 +1,13 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+import path from 'node:path'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
+ resolve: {
+ alias: {
+ '@assets': path.resolve(__dirname, '../..', 'packages/assets'),
+ },
+ },
})
diff --git a/apps/landing/app/globals.css b/apps/landing/app/globals.css
index 3df93cd..1f0c148 100644
--- a/apps/landing/app/globals.css
+++ b/apps/landing/app/globals.css
@@ -1,53 +1,8 @@
@import "tailwindcss";
@import "@repo/tailwind-config/theme.css";
-:root {
- --background: #ffffff;
- --foreground: #171717;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --background: #0a0a0a;
- --foreground: #ededed;
- }
-}
-
html,
body {
max-width: 100vw;
overflow-x: hidden;
-}
-
-body {
- color: var(--foreground);
- background: var(--background);
-}
-
-* {
- box-sizing: border-box;
- padding: 0;
- margin: 0;
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-.imgDark {
- display: none;
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- color-scheme: dark;
- }
-
- .imgLight {
- display: none;
- }
- .imgDark {
- display: unset;
- }
-}
+}
\ No newline at end of file
diff --git a/apps/landing/app/layout.tsx b/apps/landing/app/layout.tsx
index 8469537..7b653f9 100644
--- a/apps/landing/app/layout.tsx
+++ b/apps/landing/app/layout.tsx
@@ -22,7 +22,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
{children}
diff --git a/apps/landing/app/page.module.css b/apps/landing/app/page.module.css
deleted file mode 100644
index 3630662..0000000
--- a/apps/landing/app/page.module.css
+++ /dev/null
@@ -1,188 +0,0 @@
-.page {
- --gray-rgb: 0, 0, 0;
- --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
- --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
-
- --button-primary-hover: #383838;
- --button-secondary-hover: #f2f2f2;
-
- display: grid;
- grid-template-rows: 20px 1fr 20px;
- align-items: center;
- justify-items: center;
- min-height: 100svh;
- padding: 80px;
- gap: 64px;
- font-synthesis: none;
-}
-
-@media (prefers-color-scheme: dark) {
- .page {
- --gray-rgb: 255, 255, 255;
- --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
- --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
-
- --button-primary-hover: #ccc;
- --button-secondary-hover: #1a1a1a;
- }
-}
-
-.main {
- display: flex;
- flex-direction: column;
- gap: 32px;
- grid-row-start: 2;
-}
-
-.main ol {
- font-family: var(--font-geist-mono);
- padding-left: 0;
- margin: 0;
- font-size: 14px;
- line-height: 24px;
- letter-spacing: -0.01em;
- list-style-position: inside;
-}
-
-.main li:not(:last-of-type) {
- margin-bottom: 8px;
-}
-
-.main code {
- font-family: inherit;
- background: var(--gray-alpha-100);
- padding: 2px 4px;
- border-radius: 4px;
- font-weight: 600;
-}
-
-.ctas {
- display: flex;
- gap: 16px;
-}
-
-.ctas a {
- appearance: none;
- border-radius: 128px;
- height: 48px;
- padding: 0 20px;
- border: none;
- font-family: var(--font-geist-sans);
- border: 1px solid transparent;
- transition: background 0.2s, color 0.2s, border-color 0.2s;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- line-height: 20px;
- font-weight: 500;
-}
-
-a.primary {
- background: var(--foreground);
- color: var(--background);
- gap: 8px;
-}
-
-a.secondary {
- border-color: var(--gray-alpha-200);
- min-width: 180px;
-}
-
-button.secondary {
- appearance: none;
- border-radius: 128px;
- height: 48px;
- padding: 0 20px;
- border: none;
- font-family: var(--font-geist-sans);
- border: 1px solid transparent;
- transition: background 0.2s, color 0.2s, border-color 0.2s;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 16px;
- line-height: 20px;
- font-weight: 500;
- background: transparent;
- border-color: var(--gray-alpha-200);
- min-width: 180px;
-}
-
-.footer {
- font-family: var(--font-geist-sans);
- grid-row-start: 3;
- display: flex;
- gap: 24px;
-}
-
-.footer a {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.footer img {
- flex-shrink: 0;
-}
-
-/* Enable hover only on non-touch devices */
-@media (hover: hover) and (pointer: fine) {
- a.primary:hover {
- background: var(--button-primary-hover);
- border-color: transparent;
- }
-
- a.secondary:hover {
- background: var(--button-secondary-hover);
- border-color: transparent;
- }
-
- .footer a:hover {
- text-decoration: underline;
- text-underline-offset: 4px;
- }
-}
-
-@media (max-width: 600px) {
- .page {
- padding: 32px;
- padding-bottom: 80px;
- }
-
- .main {
- align-items: center;
- }
-
- .main ol {
- text-align: center;
- }
-
- .ctas {
- flex-direction: column;
- }
-
- .ctas a {
- font-size: 14px;
- height: 40px;
- padding: 0 16px;
- }
-
- a.secondary {
- min-width: auto;
- }
-
- .footer {
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .logo {
- filter: invert();
- }
-}
diff --git a/apps/landing/app/page.tsx b/apps/landing/app/page.tsx
index 23d4d59..5356054 100644
--- a/apps/landing/app/page.tsx
+++ b/apps/landing/app/page.tsx
@@ -1,8 +1,10 @@
import React from 'react'
-import "./page.module.css";
+import Nav from '../components/Header/Header'
export default function App() {
return (
- Coming Soon!
+
)
}
diff --git a/apps/landing/app/tailwind.config.ts b/apps/landing/app/tailwind.config.ts
index 357f3ec..c081153 100644
--- a/apps/landing/app/tailwind.config.ts
+++ b/apps/landing/app/tailwind.config.ts
@@ -1,9 +1,12 @@
-import tailwindThemeConfig from "@repo/tailwind-config/tailwind.config"
import type { Config } from "tailwindcss";
+import tailwindConfig from "@repo/tailwind-config/tailwind.config";
const config: Config = {
- presets: [tailwindThemeConfig],
- content: ["./src/**/*.{js,ts,jsx,tsx}"],
+ presets: [tailwindConfig],
+ content: [
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
};
export default config;
\ No newline at end of file
diff --git a/apps/landing/components/Header/DesignStripe.tsx b/apps/landing/components/Header/DesignStripe.tsx
new file mode 100644
index 0000000..416d978
--- /dev/null
+++ b/apps/landing/components/Header/DesignStripe.tsx
@@ -0,0 +1,14 @@
+'use client'
+import React from 'react'
+import { motion } from 'motion/react'
+
+export default function DesignStripe({ className }: { className: string }) {
+ return (
+
+ )
+}
diff --git a/apps/landing/components/Header/Header.tsx b/apps/landing/components/Header/Header.tsx
new file mode 100644
index 0000000..b861842
--- /dev/null
+++ b/apps/landing/components/Header/Header.tsx
@@ -0,0 +1,15 @@
+import React from 'react'
+import HeaderDesign from './HeaderDesign'
+import Nav from './Nav/Nav'
+
+export default function Header() {
+ return (
+
+ )
+}
diff --git a/apps/landing/components/Header/HeaderDesign.tsx b/apps/landing/components/Header/HeaderDesign.tsx
new file mode 100644
index 0000000..554d4de
--- /dev/null
+++ b/apps/landing/components/Header/HeaderDesign.tsx
@@ -0,0 +1,71 @@
+"use client"
+
+import React from 'react'
+import DesignStripe from './DesignStripe'
+import { motion } from 'motion/react'
+
+type HeaderDesignProps = {
+ side: 'left' | 'right'
+ className?: string
+}
+
+type StripeConfig = {
+ position: string
+ size: string
+ rotate?: string
+}
+
+// Configuration for each individual stripe on each side.
+// You can tweak these class strings to fineβtune position, size, and rotation.
+const LEFT_STRIPES: StripeConfig[] = [
+ { position: '-top-6 -left-8', size: 'w-4 h-[120%]', rotate: 'rotate-18' },
+ { position: '-top-6 -left-4', size: 'w-4 h-[120%]', rotate: 'rotate-18' },
+ { position: '-top-6 left-4', size: 'w-6 h-[110%]', rotate: 'rotate-18' },
+ { position: '-top-6 left-10', size: 'w-6 h-[100%]', rotate: 'rotate-16' },
+ { position: '-top-6 left-19', size: 'w-4 h-[85%]', rotate: 'rotate-18' },
+ { position: '-top-6 left-22', size: 'w-5 h-[110%]', rotate: 'rotate-18' },
+ { position: '-top-6 left-33', size: 'w-6 h-[100%]', rotate: 'rotate-18' },
+ { position: '-top-6 left-38', size: 'w-6 h-[120%]', rotate: 'rotate-14' },
+ { position: '-top-6 left-60', size: 'w-1 h-[100%]', rotate: 'rotate-24' },
+ { position: '-top-6 left-68', size: 'w-1 h-[120%]', rotate: 'rotate-14' },
+ { position: '-top-6 left-72', size: 'w-1 h-[100%]', rotate: 'rotate-16' },
+]
+
+const RIGHT_STRIPES: StripeConfig[] = [
+ { position: '-top-6 -right-8', size: 'w-4 h-[120%]', rotate: '-rotate-18' },
+ { position: '-top-6 -right-4', size: 'w-4 h-[120%]', rotate: '-rotate-18' },
+ { position: '-top-6 right-4', size: 'w-6 h-[110%]', rotate: '-rotate-18' },
+ { position: '-top-6 right-10', size: 'w-6 h-[100%]', rotate: '-rotate-16' },
+ { position: '-top-6 right-19', size: 'w-4 h-[85%]', rotate: '-rotate-18' },
+ { position: '-top-6 right-22', size: 'w-5 h-[110%]', rotate: '-rotate-18' },
+ { position: '-top-6 right-33', size: 'w-6 h-[100%]', rotate: '-rotate-18' },
+ { position: '-top-6 right-38', size: 'w-6 h-[120%]', rotate: '-rotate-14' },
+ { position: '-top-6 right-60', size: 'w-1 h-[100%]', rotate: '-rotate-24' },
+ { position: '-top-6 right-68', size: 'w-1 h-[120%]', rotate: '-rotate-14' },
+ { position: '-top-6 right-72', size: 'w-1 h-[100%]', rotate: '-rotate-16' },
+]
+
+export default function HeaderDesign({ side, className = '' }: HeaderDesignProps) {
+ const isLeft = side === 'left'
+
+ const stripes = isLeft ? LEFT_STRIPES : RIGHT_STRIPES
+
+ return (
+
+
+
+
+ {stripes.map((stripe, index) => (
+
+ ))}
+
+ )
+}
diff --git a/apps/landing/components/Header/Nav/GetStarted.tsx b/apps/landing/components/Header/Nav/GetStarted.tsx
new file mode 100644
index 0000000..855bbd2
--- /dev/null
+++ b/apps/landing/components/Header/Nav/GetStarted.tsx
@@ -0,0 +1,7 @@
+import React from 'react'
+
+export default function GetStarted() {
+ return (
+ GetStarted
+ )
+}
diff --git a/apps/landing/components/Header/Nav/Logo.tsx b/apps/landing/components/Header/Nav/Logo.tsx
new file mode 100644
index 0000000..0c0e6d0
--- /dev/null
+++ b/apps/landing/components/Header/Nav/Logo.tsx
@@ -0,0 +1,7 @@
+import React from 'react'
+
+export default function Logo() {
+ return (
+ Logo
+ )
+}
diff --git a/apps/landing/components/Header/Nav/Nav.tsx b/apps/landing/components/Header/Nav/Nav.tsx
new file mode 100644
index 0000000..8e5cbd6
--- /dev/null
+++ b/apps/landing/components/Header/Nav/Nav.tsx
@@ -0,0 +1,24 @@
+import React from 'react'
+import GetStarted from './GetStarted'
+import Logo from './Logo'
+import Option from './Option'
+
+const options = [
+ { href: "/", name: "Home" },
+ { href: "/about", name: "About" },
+ { href: "/contact", name: "Contact" },
+]
+
+export default function Nav() {
+ return (
+
+
+
+ {options.map((option) => (
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/landing/components/Header/Nav/Option.tsx b/apps/landing/components/Header/Nav/Option.tsx
new file mode 100644
index 0000000..7c59387
--- /dev/null
+++ b/apps/landing/components/Header/Nav/Option.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+import Link from 'next/link'
+
+export default function Option({ href, name }: { href: string, name: string }) {
+ return (
+
+
+ { name }
+
+
+ )
+}
diff --git a/apps/landing/next.config.js b/apps/landing/next.config.js
index 03e59bf..2afead6 100644
--- a/apps/landing/next.config.js
+++ b/apps/landing/next.config.js
@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
- transpilePackages: ["@repo/tailwind-theme"],
+ transpilePackages: ["@repo/tailwind-theme", "@assets"],
};
export default nextConfig;
diff --git a/apps/landing/package.json b/apps/landing/package.json
index 9d9adae..c900639 100644
--- a/apps/landing/package.json
+++ b/apps/landing/package.json
@@ -12,6 +12,8 @@
},
"dependencies": {
"@repo/ui": "*",
+ "@assets": "workspace:*",
+ "motion": "^12.23.25",
"next": "^15.5.0",
"react": "^19.1.0",
"react-dom": "^19.1.0"
diff --git a/apps/server/.env.example b/apps/server/.env.example
new file mode 100644
index 0000000..8043f37
--- /dev/null
+++ b/apps/server/.env.example
@@ -0,0 +1,22 @@
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+GOOGLE_REDIRECT_URI=
+
+DATABASE_USER=
+DATABASE_PASSWORD=
+DATABASE_HOST=
+DATABASE_PORT=
+DATABASE_NAME=
+
+ACCESS_JWT_SECRET=
+REFRESH_JWT_SECRET=
+
+ACCESS_TOKEN_COOKIE_NAME=
+REFRESH_TOKEN_COOKIE_NAME=
+ACCESS_TOKEN_EXPIRE=
+REFRESH_TOKEN_EXPIRE=
+
+CLIENT_APP_URL=
+
+HASH_ALGORITHM=
+HASH_SALT_ROUNDS=
\ No newline at end of file
diff --git a/bun.lockb b/bun.lockb
index 0261bcb..3d5dc4e 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 173d5f3..472ecd8 100644
--- a/package.json
+++ b/package.json
@@ -9,12 +9,12 @@
"check-types": "turbo run check-types"
},
"devDependencies": {
- "@tailwindcss/postcss": "^4.1.13",
- "autoprefixer": "^10.4.21",
+ "@tailwindcss/postcss": "^4.1.18",
+ "autoprefixer": "^10.4.23",
"postcss": "^8.5.6",
- "prettier": "^3.6.2",
- "tailwindcss": "^4.1.13",
- "turbo": "^2.5.8",
+ "prettier": "^3.8.1",
+ "tailwindcss": "^4.1.18",
+ "turbo": "^2.7.6",
"typescript": "5.9.2"
},
"engines": {
diff --git a/packages/assets/images.d.ts b/packages/assets/images.d.ts
new file mode 100644
index 0000000..c43d4d5
--- /dev/null
+++ b/packages/assets/images.d.ts
@@ -0,0 +1,29 @@
+declare module "*.png" {
+ const value: string;
+ export default value;
+}
+
+declare module "*.jpg" {
+ const value: string;
+ export default value;
+}
+
+declare module "*.jpeg" {
+ const value: string;
+ export default value;
+}
+
+declare module "*.gif" {
+ const value: string;
+ export default value;
+}
+
+declare module "*.svg" {
+ const value: string;
+ export default value;
+}
+
+declare module "*.webp" {
+ const value: string;
+ export default value;
+}
diff --git a/packages/assets/index.ts b/packages/assets/index.ts
new file mode 100644
index 0000000..fd9ee43
--- /dev/null
+++ b/packages/assets/index.ts
@@ -0,0 +1,3 @@
+export { default as logo } from './logo.png';
+
+
diff --git a/packages/assets/logo.png b/packages/assets/logo.png
new file mode 100644
index 0000000..76cdfea
Binary files /dev/null and b/packages/assets/logo.png differ
diff --git a/packages/assets/package.json b/packages/assets/package.json
new file mode 100644
index 0000000..0462780
--- /dev/null
+++ b/packages/assets/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@assets",
+ "version": "0.1.0",
+ "private": false,
+ "main": "dist/index.js",
+ "module": "dist/index.mjs",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc -p tsconfig.json",
+ "check-types": "tsc --noEmit"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+}
+
+
diff --git a/packages/assets/public/.gitkeep b/packages/assets/public/.gitkeep
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/packages/assets/public/.gitkeep
@@ -0,0 +1,2 @@
+
+
diff --git a/packages/assets/tsconfig.json b/packages/assets/tsconfig.json
new file mode 100644
index 0000000..a0c62fb
--- /dev/null
+++ b/packages/assets/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "@repo/typescript-config/react-library.json",
+ "compilerOptions": {
+ "outDir": "dist"
+ },
+ "include": ["index.ts", "images.d.ts"],
+ "exclude": ["node_modules", "dist"]
+}
+
+
diff --git a/packages/db/Makefile b/packages/db/Makefile
deleted file mode 100644
index fd78b9e..0000000
--- a/packages/db/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-docker-up:
- docker-compose up -d
-
-
\ No newline at end of file
diff --git a/packages/db/docker-compose.yml b/packages/db/docker-compose.yml
index c77a57f..d298cd3 100644
--- a/packages/db/docker-compose.yml
+++ b/packages/db/docker-compose.yml
@@ -1,6 +1,6 @@
services:
postgres:
- image: postgres:17-alpine
+ image: postgres:18-alpine
restart: always
environment:
- POSTGRES_USER=postgres
diff --git a/packages/db/drizzle/0001_milky_gorilla_man.sql b/packages/db/drizzle/0001_milky_gorilla_man.sql
new file mode 100644
index 0000000..fd84f70
--- /dev/null
+++ b/packages/db/drizzle/0001_milky_gorilla_man.sql
@@ -0,0 +1,37 @@
+CREATE TABLE "slice_access" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "slice_id" uuid NOT NULL,
+ "user_id" uuid NOT NULL,
+ "role" text NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "refresh_tokens" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "token_hash" varchar(256) NOT NULL,
+ "expires_at" timestamp NOT NULL,
+ "created_at" timestamp DEFAULT now() NOT NULL,
+ "revoked_at" timestamp
+);
+--> statement-breakpoint
+ALTER TABLE "slices" RENAME COLUMN "title" TO "name";--> statement-breakpoint
+ALTER TABLE "slices" RENAME COLUMN "author_id" TO "owner_id";--> statement-breakpoint
+ALTER TABLE "users" RENAME COLUMN "password" TO "password_hash";--> statement-breakpoint
+ALTER TABLE "slices" DROP CONSTRAINT "slices_author_id_users_id_fk";
+--> statement-breakpoint
+ALTER TABLE "memos" ADD COLUMN "content_type" text NOT NULL;--> statement-breakpoint
+ALTER TABLE "memos" ADD COLUMN "owner_id" uuid NOT NULL;--> statement-breakpoint
+ALTER TABLE "users" ADD COLUMN "name" varchar(256) NOT NULL;--> statement-breakpoint
+ALTER TABLE "users" ADD COLUMN "google_id" varchar(256);--> statement-breakpoint
+ALTER TABLE "slice_access" ADD CONSTRAINT "slice_access_slice_id_slices_id_fk" FOREIGN KEY ("slice_id") REFERENCES "public"."slices"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "slice_access" ADD CONSTRAINT "slice_access_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "refresh_tokens" ADD CONSTRAINT "refresh_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "memos" ADD CONSTRAINT "memos_owner_id_users_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "slices" ADD CONSTRAINT "slices_owner_id_users_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "memos" DROP COLUMN "updated_at";--> statement-breakpoint
+ALTER TABLE "slices" DROP COLUMN "updated_at";--> statement-breakpoint
+ALTER TABLE "users" DROP COLUMN "first_name";--> statement-breakpoint
+ALTER TABLE "users" DROP COLUMN "last_name";--> statement-breakpoint
+ALTER TABLE "users" DROP COLUMN "updated_at";--> statement-breakpoint
+ALTER TABLE "users" DROP COLUMN "refresh_token";--> statement-breakpoint
+ALTER TABLE "users" ADD CONSTRAINT "users_google_id_unique" UNIQUE("google_id");
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/0001_snapshot.json b/packages/db/drizzle/meta/0001_snapshot.json
new file mode 100644
index 0000000..d21d47f
--- /dev/null
+++ b/packages/db/drizzle/meta/0001_snapshot.json
@@ -0,0 +1,364 @@
+{
+ "id": "5cc85520-26f0-4db0-be94-22cbe6659dd2",
+ "prevId": "1741049b-4fbe-45ed-b4a3-ac4463ace398",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.memos": {
+ "name": "memos",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content_type": {
+ "name": "content_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slice_id": {
+ "name": "slice_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "memos_owner_id_users_id_fk": {
+ "name": "memos_owner_id_users_id_fk",
+ "tableFrom": "memos",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "memos_slice_id_slices_id_fk": {
+ "name": "memos_slice_id_slices_id_fk",
+ "tableFrom": "memos",
+ "tableTo": "slices",
+ "columnsFrom": [
+ "slice_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_email_unique": {
+ "name": "users_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ },
+ "users_google_id_unique": {
+ "name": "users_google_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "google_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.slices": {
+ "name": "slices",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "slices_owner_id_users_id_fk": {
+ "name": "slices_owner_id_users_id_fk",
+ "tableFrom": "slices",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.slice_access": {
+ "name": "slice_access",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slice_id": {
+ "name": "slice_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "slice_access_slice_id_slices_id_fk": {
+ "name": "slice_access_slice_id_slices_id_fk",
+ "tableFrom": "slice_access",
+ "tableTo": "slices",
+ "columnsFrom": [
+ "slice_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "slice_access_user_id_users_id_fk": {
+ "name": "slice_access_user_id_users_id_fk",
+ "tableFrom": "slice_access",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.refresh_tokens": {
+ "name": "refresh_tokens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "revoked_at": {
+ "name": "revoked_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "refresh_tokens_user_id_users_id_fk": {
+ "name": "refresh_tokens_user_id_users_id_fk",
+ "tableFrom": "refresh_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json
index ccebc75..1d7b042 100644
--- a/packages/db/drizzle/meta/_journal.json
+++ b/packages/db/drizzle/meta/_journal.json
@@ -8,6 +8,13 @@
"when": 1760458602500,
"tag": "0000_swift_quasimodo",
"breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "7",
+ "when": 1769503693113,
+ "tag": "0001_milky_gorilla_man",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/db/package.json b/packages/db/package.json
index 0372bd2..cfd268b 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -1,12 +1,16 @@
{
- "name": "db",
- "module": ".src/index.ts",
+ "name": "@repo/db",
+ "module": "./src/index.ts",
"scripts": {
"drizzle:generate": "drizzle-kit generate",
"drizzle:push": "drizzle-kit push",
"drizzle:studio": "drizzle-kit studio",
"drizzle:migrate": "drizzle-kit migrate"
},
+ "exports": {
+ ".": "./src/index.ts"
+ },
+ "types": "./src/index.ts",
"type": "module",
"devDependencies": {
"@types/bun": "latest",
diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts
index 6db665d..663ffaa 100644
--- a/packages/db/src/index.ts
+++ b/packages/db/src/index.ts
@@ -1,6 +1,14 @@
-// Make sure to install the 'pg' package
-import { drizzle } from 'drizzle-orm/node-postgres';
+import { drizzle } from "drizzle-orm/node-postgres";
+import * as schema from "./schema";
-const db = drizzle(process.env.DATABASE_URL as string);
+const db = drizzle(process.env.DATABASE_URL as string, { schema });
-export { db }
\ No newline at end of file
+export { db, schema };
+export * from "./schema";
+
+// Explicit exports for better TypeScript resolution
+export { users, userRelations } from "./schema/user";
+export { slices, sliceRelations } from "./schema/slice";
+export { sliceAccess, sliceAccessRelations } from "./schema/sliceAccess";
+export { memos, memoRelations } from "./schema/memo";
+export { refreshTokens, refreshTokenRelations } from "./schema/refreshToken";
diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts
new file mode 100644
index 0000000..6250c26
--- /dev/null
+++ b/packages/db/src/schema/index.ts
@@ -0,0 +1,8 @@
+export * from "./user";
+export * from "./slice";
+export * from "./sliceAccess";
+export * from "./memo";
+export * from "./refreshToken";
+
+// Since brain and memo are the same per user requirement
+export { memos as brains, memoRelations as brainRelations } from "./memo";
diff --git a/packages/db/src/schema/memo.ts b/packages/db/src/schema/memo.ts
index 7dfb252..8444a34 100644
--- a/packages/db/src/schema/memo.ts
+++ b/packages/db/src/schema/memo.ts
@@ -1,15 +1,17 @@
import { pgTable, uuid, text, timestamp, primaryKey } from "drizzle-orm/pg-core"
import { relations, sql } from "drizzle-orm"
import { slices } from "./slice"
+import { users } from "./user"
export const memos = pgTable('memos', {
id: uuid('id').primaryKey().default(sql`gen_random_uuid()`).notNull(),
title: text('title').notNull(),
description: text('description').notNull(),
+ contentType: text('content_type').notNull(), // "text" | "url" | "image" | "video" | "file"
content: text('content').notNull(),
+ ownerId: uuid('owner_id').references(() => users.id).notNull(),
sliceId: uuid('slice_id').references(() => slices.id).notNull(),
createdAt: timestamp("created_at").default(sql`now()`).notNull(),
- updatedAt: timestamp("updated_at").default(sql`now()`).notNull(),
})
export const memoRelations = relations(memos, ({ one }) => (
@@ -17,6 +19,10 @@ export const memoRelations = relations(memos, ({ one }) => (
slice: one(slices, {
fields: [memos.sliceId],
references: [slices.id],
+ }),
+ owner: one(users, {
+ fields: [memos.ownerId],
+ references: [users.id],
})
}
))
\ No newline at end of file
diff --git a/packages/db/src/schema/refreshToken.ts b/packages/db/src/schema/refreshToken.ts
new file mode 100644
index 0000000..3ca0e34
--- /dev/null
+++ b/packages/db/src/schema/refreshToken.ts
@@ -0,0 +1,31 @@
+import {
+ pgTable,
+ uuid,
+ varchar,
+ timestamp,
+} from "drizzle-orm/pg-core";
+import { relations, sql } from "drizzle-orm";
+import { users } from "./user";
+
+export const refreshTokens = pgTable("refresh_tokens", {
+ id: uuid("id")
+ .primaryKey()
+ .default(sql`gen_random_uuid()`)
+ .notNull(),
+ userId: uuid("user_id")
+ .references(() => users.id)
+ .notNull(),
+ tokenHash: varchar("token_hash", { length: 256 }).notNull(),
+ expiresAt: timestamp("expires_at").notNull(),
+ createdAt: timestamp("created_at")
+ .default(sql`now()`)
+ .notNull(),
+ revokedAt: timestamp("revoked_at"),
+});
+
+export const refreshTokenRelations = relations(refreshTokens, ({ one }) => ({
+ user: one(users, {
+ fields: [refreshTokens.userId],
+ references: [users.id],
+ }),
+}));
diff --git a/packages/db/src/schema/slice.ts b/packages/db/src/schema/slice.ts
index b952d5d..ce2e782 100644
--- a/packages/db/src/schema/slice.ts
+++ b/packages/db/src/schema/slice.ts
@@ -1,28 +1,33 @@
-import { pgTable, uuid, text, timestamp, primaryKey } from "drizzle-orm/pg-core"
-import { relations, sql } from "drizzle-orm"
-import { users } from "./user"
-import { memos } from "./memo"
+import {
+ pgTable,
+ uuid,
+ text,
+ timestamp,
+ primaryKey,
+} from "drizzle-orm/pg-core";
+import { relations, sql } from "drizzle-orm";
+import { users } from "./user";
+import { memos } from "./memo";
-export const slices = pgTable('slices', {
- id: uuid('id').primaryKey().default(sql`gen_random_uuid()`).notNull(),
- title: text('title').notNull(),
- description: text('description').notNull(),
- authorId: uuid('author_id').references(() => users.id).notNull(),
- createdAt: timestamp("created_at").default(sql`now()`).notNull(),
- updatedAt: timestamp("updated_at").default(sql`now()`).notNull(),
-})
+export const slices = pgTable("slices", {
+ id: uuid("id")
+ .primaryKey()
+ .default(sql`gen_random_uuid()`)
+ .notNull(),
+ name: text("name").notNull(),
+ description: text("description").notNull(),
+ ownerId: uuid("owner_id")
+ .references(() => users.id)
+ .notNull(),
+ createdAt: timestamp("created_at")
+ .default(sql`now()`)
+ .notNull(),
+});
-export const userSlices = relations(slices, ({ one }) => (
- {
- author: one(users, {
- fields: [slices.authorId],
- references: [users.id],
- })
- }
-))
-
-export const sliceMemos = relations(slices, ({ many }) => (
- {
- memos: many(memos),
- }
-))
\ No newline at end of file
+export const sliceRelations = relations(slices, ({ one, many }) => ({
+ owner: one(users, {
+ fields: [slices.ownerId],
+ references: [users.id],
+ }),
+ memos: many(memos),
+}));
diff --git a/packages/db/src/schema/sliceAccess.ts b/packages/db/src/schema/sliceAccess.ts
new file mode 100644
index 0000000..2299566
--- /dev/null
+++ b/packages/db/src/schema/sliceAccess.ts
@@ -0,0 +1,24 @@
+import { pgTable, uuid, text, primaryKey } from "drizzle-orm/pg-core"
+import { relations, sql } from "drizzle-orm"
+import { users } from "./user"
+import { slices } from "./slice"
+
+export const sliceAccess = pgTable('slice_access', {
+ id: uuid('id').default(sql`gen_random_uuid()`).primaryKey().notNull(),
+ sliceId: uuid('slice_id').references(() => slices.id).notNull(),
+ userId: uuid('user_id').references(() => users.id).notNull(),
+ role: text('role').notNull(), // "viewer" | "editor"
+})
+
+export const sliceAccessRelations = relations(sliceAccess, ({ one }) => (
+ {
+ slice: one(slices, {
+ fields: [sliceAccess.sliceId],
+ references: [slices.id],
+ }),
+ user: one(users, {
+ fields: [sliceAccess.userId],
+ references: [users.id],
+ })
+ }
+))
diff --git a/packages/db/src/schema/user.ts b/packages/db/src/schema/user.ts
index 4e25369..b76babb 100644
--- a/packages/db/src/schema/user.ts
+++ b/packages/db/src/schema/user.ts
@@ -1,20 +1,21 @@
-import { pgTable, varchar, uuid, timestamp, index } from "drizzle-orm/pg-core"
-import { relations, sql } from "drizzle-orm"
-import { slices } from "./slice"
+import { pgTable, varchar, uuid, timestamp, index } from "drizzle-orm/pg-core";
+import { relations, sql } from "drizzle-orm";
+import { slices } from "./slice";
-export const users = pgTable('users', {
- id: uuid('id').primaryKey().default(sql`gen_random_uuid()`).notNull(),
- firstName: varchar("first_name", { length: 256 }).notNull(),
- lastName: varchar("last_name", { length: 256 }).notNull(),
+export const users = pgTable("users", {
+ id: uuid("id")
+ .primaryKey()
+ .default(sql`gen_random_uuid()`)
+ .notNull(),
+ name: varchar("name", { length: 256 }).notNull(),
email: varchar("email", { length: 256 }).notNull().unique(),
- password: varchar("password", { length: 256 }).notNull(),
- createdAt: timestamp("created_at").default(sql`now()`).notNull(),
- updatedAt: timestamp("updated_at").default(sql`now()`).notNull(),
- refreshToken: varchar("refresh_token", { length: 256 }).notNull(),
-})
+ googleId: varchar("google_id", { length: 256 }).unique(),
+ passwordHash: varchar("password_hash", { length: 256 }),
+ createdAt: timestamp("created_at")
+ .default(sql`now()`)
+ .notNull(),
+});
-export const userSlices = relations(users, ({ many }) => (
- {
- slices: many(slices),
- }
-))
\ No newline at end of file
+export const userRelations = relations(users, ({ many }) => ({
+ slices: many(slices),
+}));
diff --git a/packages/tailwind-config/theme.css b/packages/tailwind-config/theme.css
index cc47852..8521036 100644
--- a/packages/tailwind-config/theme.css
+++ b/packages/tailwind-config/theme.css
@@ -1,13 +1,10 @@
-@layer base {
- :root {
- --color-primary: #2563eb;
- --color-secondary: #9333ea;
- --color-background: #f9fafb;
- --color-text: #1f2937;
- }
+@theme {
+ --color-primary: #2563eb;
+ --color-secondary: #9333ea;
+ --color-background: #f9fafb;
+ --color-text: #1f2937;
+}
- body {
- background-color: var(--color-background);
- color: var(--color-text);
- }
+@layer base {
+
}
\ No newline at end of file
diff --git a/packages/types/README.md b/packages/types/README.md
new file mode 100644
index 0000000..d4ce042
--- /dev/null
+++ b/packages/types/README.md
@@ -0,0 +1,36 @@
+# @packages/types
+
+TypeScript type definitions generated from database schemas.
+
+## Usage
+
+```typescript
+import type { User, NewUser, Slice, Memo, RefreshToken } from 'types';
+
+// Use the types in your application
+const user: User = {
+ id: '...',
+ name: 'John Doe',
+ email: 'john@example.com',
+ // ...
+};
+```
+
+## Available Types
+
+### Database Models
+
+- `User` / `NewUser` - User table types
+- `Slice` / `NewSlice` - Slice table types
+- `SliceAccess` / `NewSliceAccess` - Slice access control types
+- `Memo` / `NewMemo` - Memo table types
+- `RefreshToken` / `NewRefreshToken` - Refresh token types
+
+### Enums
+
+- `ContentType` - "text" | "url" | "image" | "video" | "file"
+- `SliceAccessRole` - "viewer" | "editor"
+
+## Notes
+
+Types are automatically inferred from the database schema using Drizzle ORM's `InferSelectModel` and `InferInsertModel` utilities.
diff --git a/packages/types/package.json b/packages/types/package.json
new file mode 100644
index 0000000..6b01507
--- /dev/null
+++ b/packages/types/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@repo/types",
+ "version": "0.0.0",
+ "private": true,
+ "main": "./src/index.ts",
+ "types": "./src/index.ts",
+ "exports": {
+ ".": "./src/index.ts"
+ },
+ "scripts": {
+ "typecheck": "tsc --noEmit"
+ },
+ "devDependencies": {
+ "@types/node": "^20.0.0",
+ "typescript": "^5.0.0"
+ },
+ "dependencies": {
+ "@repo/db": "workspace:*",
+ "drizzle-orm": "^0.44.6"
+ }
+}
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
new file mode 100644
index 0000000..96fe994
--- /dev/null
+++ b/packages/types/src/index.ts
@@ -0,0 +1,36 @@
+import type { InferSelectModel, InferInsertModel } from 'drizzle-orm';
+import {
+ users,
+ slices,
+ sliceAccess,
+ memos,
+ refreshTokens
+} from '@repo/db';
+
+// User types
+export type User = InferSelectModel;
+export type NewUser = InferInsertModel;
+
+// Slice types
+export type Slice = InferSelectModel;
+export type NewSlice = InferInsertModel;
+
+// SliceAccess types
+export type SliceAccess = InferSelectModel;
+export type NewSliceAccess = InferInsertModel;
+
+// Memo / Brain types
+export type Memo = InferSelectModel;
+export type NewMemo = InferInsertModel;
+export type Brain = Memo;
+export type NewBrain = NewMemo;
+
+// RefreshToken types
+export type RefreshToken = InferSelectModel;
+export type NewRefreshToken = InferInsertModel;
+
+// Content type enum
+export type ContentType = "text" | "url" | "image" | "video" | "file";
+
+// SliceAccess role enum
+export type SliceAccessRole = "viewer" | "editor";
diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json
new file mode 100644
index 0000000..4657fd5
--- /dev/null
+++ b/packages/types/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../typescript-config/base.json",
+ "compilerOptions": {
+ "outDir": "./dist",
+ "rootDir": "./src"
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}