Presk:AI 原生生产力平台,集成日历、项目、文件与消息
preskater/presk
Presk 是一个 AI 原生工作空间,将日历、项目、文件和消息整合到一个平台,内置 AI 助手可起草文档、总结讨论串并自动执行重复性工作。
AI‑Native Productivity Platform
Presk is an AI‑first workspace that automates your workflow, centralizes your tasks, and connects your team in one place. It combines calendars, projects, files, and messaging with built‑in AI assistants to help you move faster and stay aligned.
What you can do with Presk
- Calendars – Schedule meetings, set reminders, and manage events across teams.
- Projects – Create projects, assign tasks, track progress, and manage deadlines.
- Files – Create, edit, and co‑author documents in real time with your team and AI.
- Messages – Chat with teammates and AI assistants in shared channels and threads.
- Dashboard – See all your tasks, projects, and team activity in a single, customizable view.
Why Presk
- AI‑native: AI is embedded in every surface—drafting docs, summarizing threads, suggesting next actions, and automating repetitive work.
- Unified workspace: No more switching between tools; everything you need lives in one platform.
- Team‑first: Built for collaboration, with real‑time editing, shared context, and clear ownership.
Tech stack
- Framework: Next.js (App Router) + React 19
- Monorepo: Turborepo with npm workspaces
- Database: PostgreSQL via Prisma 7 (driver adapter
@prisma/adapter-pg) - Auth: Better Auth
- UI: shadcn/ui + Base UI + Tailwind CSS
- AI: OpenAI Agents SDK, exposed to clients over MCP
- i18n: next-intl (English and French)
Repository structure
| Path | Description |
|---|---|
apps/web |
Next.js application (App Router, i18n, API + MCP routes). |
packages/ui |
Shared UI component library. |
packages/eslint-config |
Shared ESLint configuration. |
packages/typescript-config |
Shared TypeScript configuration. |
Getting started
Prerequisites
- Node.js >= 20.9.0
- npm 11 (declared via
packageManager; runcorepack enableto match) - Docker (recommended) for a local PostgreSQL instance
Setup
-
Install dependencies from the repository root:
npm install
-
Start PostgreSQL:
docker compose up -d postgres
-
Create your environment file and fill in the values:
cp apps/web/.env.example apps/web/.env
At minimum set
DATABASE_URLandBETTER_AUTH_SECRET. AnOPENAI_API_KEYis only required for AI assistant features. See Environment variables below. -
Generate the Prisma client and apply migrations:
npm run generate --workspace=web npx prisma migrate deploy --schema apps/web/prisma/schema.prisma
-
Start the dev server:
npm run dev
The app is available at http://localhost:3000.
Prefer containers?
docker compose up --buildruns PostgreSQL, applies migrations, and serves the production build on port 3000.
Environment variables
All variables live in apps/web/.env (see apps/web/.env.example).
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string. |
DATABASE_POOL_MAX |
No | Max Postgres connections per instance (default 3). |
BETTER_AUTH_SECRET |
Yes | Secret used to sign sessions. |
BETTER_AUTH_URL |
Yes | Public base URL of the app. |
OPENAI_API_KEY |
No | Enables AI assistant features. |
OPENAI_API_URL |
No | Custom OpenAI-compatible base URL. |
ASSISTANT_MODEL |
No | Model used by the assistant (default gpt-4o-mini). |
MCP_RESOURCE_URL |
No | Public identifier of the OAuth2-protected /mcp endpoint (defaults to ${BETTER_AUTH_URL}/mcp). |
Available scripts
Run these from the repository root; Turborepo fans them out to the relevant workspaces.
| Command | Description |
|---|---|
npm run dev |
Start the dev servers in watch mode. |
npm run build |
Build all workspaces. |
npm run lint |
Lint all workspaces. |
npm run typecheck |
Type-check all workspaces. |
npm run format |
Format with Prettier. |
File storage
Uploaded files are stored as PostgreSQL large objects (pg_largeobject) in the
same database as their metadata. There is no external object storage (Vercel
Blob has been removed).
- Bytes → large objects.
lib/large-object.tsstreams bytes into a large object and streams them back out. Responses useReadable.toWeb(...); nothing is buffered in memory. - Chunked uploads (Vercel-safe). Vercel caps a Function request body at
4.5 MB, so a file is uploaded in 3 MiB chunks under an
UploadSessioninstead of one request:POST /api/files/uploads— authz + quota check, creates the large object and anUploadSession(name,parentId,mimeType,expectedSize,transient).PUT /api/files/uploads/[uploadId]— appends one raw chunk; thex-chunk-offsetheader must equal the bytes already received (rejects gaps, duplicates, and reordering). Retries are safe.POST /api/files/uploads/[uploadId]/complete— re-reads the stored bytes to validate size + SHA‑256, then creates theFileNode/FileVersion(or returns theoidfor a transient message attachment).DELETE /api/files/uploads/[uploadId]— aborts and unlinks the large object.GET /api/files/uploads/sweep— unlinks sessions older than their lease (CRON_SECRET-guarded when set; also runs opportunistically on init).
- Metadata → Prisma.
FileNode,FileVersion, andMessageAttachmentholdoid BigInt? @unique,sizeBytes,mimeType, andsha256.UploadSessiontracks in-progress uploads. - One shared pool.
lib/prisma.tsexports a singlepg.Poolhanded tonew PrismaPg(pool, { disposeExternalPool: false }); Prisma and the large-object code share it. Each large-object operation runs on one dedicated connection inside a singleBEGIN … COMMITtransaction (neverpool.query()). - Downloads stream the large object with
Content-Length/Content-Disposition;maxDurationis set on the upload/download routes.
Operational caveats
- Connections are held for the duration of a transfer. Each in-flight
upload/download occupies one pooled connection (default pool max is 3). Raise
DATABASE_POOL_MAXif transfers run concurrently with Prisma traffic, or use a separate pool for large objects. PointDATABASE_URLat a pooler in production. - No fixed per-file cap. Uploads are bounded only by the organization's remaining storage quota (free 1 GiB / team 100 GiB / enterprise 1 TiB). A large download holds a pooled connection for the whole transfer, so very large files are best served from a dedicated media host.
- In-memory retry only. A failed chunk is retried within the session; uploads do not resume across page reloads. Abandoned sessions are unlinked after their lease expires.
- No Blob migration. Existing
storageKeyvalues were dropped in the20261009140000_replace_blob_with_large_objectmigration; old Blob bytes are not imported. - Deletes unlink the backing large object(s) synchronously on a best-effort basis and log failures without failing the request.
- SSL warning. Set
sslmode=verify-full(oruselibpqcompat=true&sslmode=require) inDATABASE_URLto silence thepg-connection-stringdeprecation warning on cold start.
Verifying
apps/web/scripts/verify-large-object.ts uploads ≥8 MiB of random bytes, reads
them back, and asserts equal size and SHA‑256 for both the single-shot and the
chunked create/append/hash paths; it then deletes/aborts and asserts
pg_largeobject_metadata returns to its baseline (no orphans):
npm run verify:large-object --workspace=web
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for setup instructions and our workflow, and note our Code of Conduct. To report a security issue, see SECURITY.md.
License
Released under the MIT License.
来源:GitHub · github.com