diff --git a/.gitignore b/.gitignore index 04c01ba..7b11b3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ node_modules/ -dist/ \ No newline at end of file +dist/ + +# Business documentation (contains sensitive info) +docs/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ac58b6f --- /dev/null +++ b/LICENSE @@ -0,0 +1,47 @@ +MIT License + +Copyright (c) 2024 Rohith Gilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +## Commercial Use License + +While the source code is MIT licensed, pre-built binaries require a license +for commercial use. Commercial use is defined as using data-peek for +revenue-generating or work-related activities within a for-profit organization +of two or more people. + +### Free Use (No License Required) +- Personal projects and learning +- Open source contributors +- Students and educators +- Registered non-profit organizations +- Solo founders (company of one) + +### License Required +- Developers at companies with 2+ people +- Freelancers billing clients +- Agencies using for client work + +This licensing model is inspired by Yaak and other sustainable indie software +projects that balance open source values with sustainable development. + +Learn more at https://data-peek.dev/license diff --git a/apps/web/src/components/marketing/faq.tsx b/apps/web/src/components/marketing/faq.tsx index 10c89b4..a99055d 100644 --- a/apps/web/src/components/marketing/faq.tsx +++ b/apps/web/src/components/marketing/faq.tsx @@ -7,42 +7,52 @@ const faqs = [ { question: 'Is data-peek really free?', answer: - 'Yes! The free tier is fully functional with 2 connections, 50 query history items, and 3 tabs. No credit card required, no time limits. The Pro license unlocks unlimited everything plus advanced features like inline editing and query plans.', + 'Yes! data-peek is free for personal use with all features unlocked. No credit card required, no time limits, no feature restrictions. A license is only required if you use it for commercial purposes at a for-profit company with 2+ people.', }, { - question: 'What does "pay once, own forever" mean?', + question: 'What counts as commercial use?', answer: - 'Unlike subscription software, you pay $29 once and get a perpetual license. The license includes 1 year of updates. After that year, you can keep using the version you have forever, or renew for another year of updates at a discounted rate.', + 'Commercial use means using data-peek for work-related activities in a for-profit organization of 2+ people. This includes developers at startups/companies, freelancers billing clients, and agencies doing client work. Solo founders (company of one) are free!', }, { - question: 'How many devices can I use with one license?', + question: 'Is data-peek open source?', answer: - 'One Pro license includes 3 device activations. You can use data-peek on your work laptop, home computer, and one more device. Need more? Contact us for volume licensing.', + 'Yes! The source code is MIT licensed on GitHub. You can view, modify, fork, and build it yourself for any purpose. Pre-built binaries require a license for commercial use — this is how we sustain development.', }, { - question: 'Does data-peek work offline?', + question: 'What does "perpetual fallback" mean?', answer: - "Absolutely. data-peek runs entirely on your machine. We validate licenses online during activation, but after that, you can work offline. There's a grace period for license revalidation.", + 'When you buy a Pro license ($29/year), you get 1 year of updates. If you don\'t renew, you keep your current version forever — it doesn\'t stop working. You just won\'t receive future updates. Renew anytime to get the latest.', }, { - question: 'What databases are supported?', + question: "I'm a student. Can I use it for free?", answer: - "Currently, data-peek is PostgreSQL-only. We're laser-focused on making the best Postgres experience possible. MySQL and SQLite support are planned for future releases.", + 'Absolutely! Students and educators can use data-peek for free, even for school projects. Just reach out on X (@gillarohith) or email gillarohith1@gmail.com and we\'ll hook you up with a free license. Learning should never have barriers.', + }, + { + question: 'How does the honor system work?', + answer: + 'We trust you. There\'s no DRM, no aggressive license checks, no "you\'ve been logged out" surprises. If you\'re using it commercially, we ask that you pay for a license. Inspired by Yaak and other sustainable indie software.', + }, + { + question: 'How many devices can I use?', + answer: + 'Each license includes 3 device activations. Use it on your work laptop, home computer, and one more device. Need more? Just reach out.', }, { question: 'Is my data safe?', answer: - 'Yes. data-peek never sends your data anywhere. All queries run directly from your machine to your database. Connection credentials are encrypted locally. We have no telemetry, no analytics, no tracking.', + 'Yes. data-peek runs entirely on your machine. All queries go directly to your database — we never see your data. Connection credentials are encrypted locally. No telemetry, no analytics, no tracking.', + }, + { + question: 'What databases are supported?', + answer: + 'Currently PostgreSQL and MySQL. We\'re laser-focused on making the best database experience possible. SQLite and more databases are planned for future releases.', }, { question: 'Can I get a refund?', answer: - "Yes, we offer a 14-day money-back guarantee. If data-peek isn't right for you, just email us and we'll refund your purchase, no questions asked.", - }, - { - question: 'Do you offer team or enterprise licenses?', - answer: - "Not yet, but we're working on it! Cloud sync and team features are coming soon. Sign up for our newsletter to be notified when they launch.", + 'Yes, 30-day money-back guarantee, no questions asked. If data-peek isn\'t right for you, just email us.', }, ] diff --git a/apps/web/src/components/marketing/footer.tsx b/apps/web/src/components/marketing/footer.tsx index c2f5c09..43bdc00 100644 --- a/apps/web/src/components/marketing/footer.tsx +++ b/apps/web/src/components/marketing/footer.tsx @@ -57,7 +57,7 @@ export function Footer() {
- {/* Early Bird Badge */} -
- + {/* Early Bird + Open Source Badge */} +
+ Early Bird — 70% off + + + Open Source +
{/* Main Headline */} @@ -52,7 +56,7 @@ export function Hero() { style={{ fontFamily: 'var(--font-body)' }} > A lightning-fast PostgreSQL client for developers who value simplicity. - No bloat, no subscriptions, no BS. + Open source, free for personal use.

{/* Terminal-style feature highlight */} @@ -70,7 +74,7 @@ export function Hero() { - pay once, own forever + all features free
diff --git a/apps/web/src/components/marketing/pricing.tsx b/apps/web/src/components/marketing/pricing.tsx index 00aa084..7e6ad52 100644 --- a/apps/web/src/components/marketing/pricing.tsx +++ b/apps/web/src/components/marketing/pricing.tsx @@ -1,23 +1,22 @@ import Link from 'next/link' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' -import { Check, X, Sparkles, ArrowRight } from 'lucide-react' +import { Check, Heart, Github, ArrowRight, Sparkles } from 'lucide-react' const plans = [ { - name: 'Free', + name: 'Personal', price: '$0', period: 'forever', - description: 'Perfect for trying out data-peek', + description: 'All features, personal use only', features: [ - { text: '2 database connections', included: true }, - { text: '50 query history items', included: true }, - { text: '3 editor tabs', included: true }, - { text: '1 schema for ER diagrams', included: true }, - { text: 'CSV/JSON export', included: true }, - { text: 'Inline data editing', included: false }, - { text: 'Query execution plans', included: false }, - { text: 'Updates', included: false }, + { text: 'All features unlocked', included: true }, + { text: 'Unlimited connections', included: true }, + { text: 'Unlimited query history', included: true }, + { text: 'All future updates', included: true }, + { text: 'Personal projects & learning', included: true }, + { text: 'Open source contributors', included: true }, + { text: 'Students & educators', included: true }, ], cta: 'Download Free', href: '/download', @@ -27,19 +26,17 @@ const plans = [ name: 'Pro', price: '$29', originalPrice: '$99', - period: 'one-time', - description: 'For developers who need the full power', + period: 'year', + description: 'For commercial use at work', badge: 'Early Bird — 70% off', features: [ - { text: 'Unlimited connections', included: true }, - { text: 'Unlimited query history', included: true }, - { text: 'Unlimited tabs', included: true }, - { text: 'Unlimited ER diagrams', included: true }, - { text: 'CSV/JSON export', included: true }, - { text: 'Inline data editing', included: true }, - { text: 'Query execution plans', included: true }, - { text: '1 year of updates', included: true }, + { text: 'Everything in Personal', included: true }, + { text: 'Commercial use allowed', included: true }, + { text: 'Use at work & for clients', included: true }, + { text: '1 year of updates included', included: true }, { text: '3 device activations', included: true }, + { text: 'Perpetual fallback license', included: true }, + { text: '30-day money-back guarantee', included: true }, ], cta: 'Get Pro License', href: '#buy', @@ -66,15 +63,16 @@ export function Pricing() { className="text-4xl md:text-5xl lg:text-6xl font-semibold tracking-tight mb-6" style={{ fontFamily: 'var(--font-display)' }} > - Pay once. + Open source.
- Own forever. + Pay for commercial use.

- No subscriptions, no recurring fees. One purchase, lifetime access. + Free for personal use. A license supports development and is required + for commercial use in for-profit organizations.

@@ -92,7 +90,7 @@ export function Pricing() { {/* Badge */} {plan.badge && (
- + {plan.badge} @@ -128,22 +126,10 @@ export function Pricing() {
    {plan.features.map((feature) => (
  • - {feature.included ? ( -
    - -
    - ) : ( -
    - -
    - )} - +
    + +
    + {feature.text}
  • @@ -166,20 +152,69 @@ export function Pricing() { ))}
- {/* Cloud Teaser */} -
-
- - Coming soon: - - - Cloud sync & team features - + {/* Honor System Notice */} +
+
+
+ +
+
+

+ Honor System Licensing +

+

+ Inspired by{' '} + + Yaak + {' '} + and other sustainable indie software. No DRM, no aggressive enforcement. + We trust you to do the right thing. +

+

+ Students: Use it free! Just reach out for a free license. +

+

+ Questions?{' '} + + @gillarohith + {' '} + or{' '} + + gillarohith1@gmail.com + +

+
+ + {/* Open Source Notice */} +
+ + + View source on GitHub — MIT Licensed + +
) diff --git a/docs/PLAN-table-designer.md b/docs/PLAN-table-designer.md deleted file mode 100644 index bece7ae..0000000 --- a/docs/PLAN-table-designer.md +++ /dev/null @@ -1,570 +0,0 @@ -# Table Designer Feature - Implementation Plan - -## Overview - -This plan outlines the implementation of a comprehensive Table Designer feature for data-peek, enabling users to create new tables and modify existing tables through a dedicated tab interface with full PostgreSQL feature support. - -## Scope - -- **Create Table**: Full-featured table creation with columns, constraints, indexes -- **Edit Table (ALTER TABLE)**: Add/drop/rename columns, modify types, manage constraints and indexes -- **PostgreSQL Full Features**: Sequences, partitioning, inheritance, custom types, CHECK constraints, UNIQUE constraints - ---- - -## Phase 1: Shared Types & Data Models - -### File: `packages/shared/src/index.ts` - -Add new types for DDL operations: - -```typescript -// PostgreSQL data types for dropdown -export type PostgresDataType = - | 'smallint' | 'integer' | 'bigint' | 'serial' | 'bigserial' - | 'numeric' | 'real' | 'double precision' | 'money' - | 'char' | 'varchar' | 'text' - | 'bytea' - | 'timestamp' | 'timestamptz' | 'date' | 'time' | 'timetz' | 'interval' - | 'boolean' - | 'uuid' - | 'json' | 'jsonb' - | 'xml' - | 'point' | 'line' | 'lseg' | 'box' | 'path' | 'polygon' | 'circle' - | 'cidr' | 'inet' | 'macaddr' - | 'int4range' | 'int8range' | 'numrange' | 'tsrange' | 'tstzrange' | 'daterange' - | 'custom' // For custom/enum types - -// Column definition for table designer -export interface ColumnDefinition { - id: string // Client-side tracking - name: string - dataType: PostgresDataType | string - length?: number // For varchar(n), char(n) - precision?: number // For numeric(p,s) - scale?: number - isNullable: boolean - isPrimaryKey: boolean - isUnique: boolean - defaultValue?: string - defaultType?: 'value' | 'expression' | 'sequence' - sequenceName?: string // For nextval('sequence') - checkConstraint?: string - comment?: string - collation?: string - isArray?: boolean // For array types like text[] -} - -// Constraint types -export type ConstraintType = 'primary_key' | 'foreign_key' | 'unique' | 'check' | 'exclude' - -export interface ConstraintDefinition { - id: string - name?: string // Optional, auto-generated if not provided - type: ConstraintType - columns: string[] - // Foreign key specific - referencedSchema?: string - referencedTable?: string - referencedColumns?: string[] - onUpdate?: 'NO ACTION' | 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'SET DEFAULT' - onDelete?: 'NO ACTION' | 'RESTRICT' | 'CASCADE' | 'SET NULL' | 'SET DEFAULT' - // Check constraint specific - checkExpression?: string - // Exclude constraint specific (advanced) - excludeElements?: Array<{ column: string; operator: string }> - excludeUsing?: 'btree' | 'gist' | 'gin' | 'hash' -} - -// Index definition -export interface IndexDefinition { - id: string - name?: string - columns: Array<{ - name: string - order?: 'ASC' | 'DESC' - nullsPosition?: 'FIRST' | 'LAST' - }> - isUnique: boolean - method?: 'btree' | 'hash' | 'gist' | 'gin' | 'spgist' | 'brin' - where?: string // Partial index condition - include?: string[] // INCLUDE columns (covering index) - concurrent?: boolean -} - -// Table partitioning -export interface PartitionDefinition { - type: 'RANGE' | 'LIST' | 'HASH' - columns: string[] -} - -// Full table definition -export interface TableDefinition { - schema: string - name: string - columns: ColumnDefinition[] - constraints: ConstraintDefinition[] - indexes: IndexDefinition[] - partition?: PartitionDefinition - inherits?: string[] // Parent tables - tablespace?: string - comment?: string - withOids?: boolean - unlogged?: boolean -} - -// ALTER TABLE operations -export type AlterColumnOperation = - | { type: 'add'; column: ColumnDefinition } - | { type: 'drop'; columnName: string; cascade?: boolean } - | { type: 'rename'; oldName: string; newName: string } - | { type: 'set_type'; columnName: string; newType: string; using?: string } - | { type: 'set_nullable'; columnName: string; nullable: boolean } - | { type: 'set_default'; columnName: string; defaultValue: string | null } - | { type: 'set_comment'; columnName: string; comment: string | null } - -export type AlterConstraintOperation = - | { type: 'add_constraint'; constraint: ConstraintDefinition } - | { type: 'drop_constraint'; name: string; cascade?: boolean } - | { type: 'rename_constraint'; oldName: string; newName: string } - -export type AlterIndexOperation = - | { type: 'create_index'; index: IndexDefinition } - | { type: 'drop_index'; name: string; cascade?: boolean; concurrent?: boolean } - | { type: 'rename_index'; oldName: string; newName: string } - | { type: 'reindex'; name: string; concurrent?: boolean } - -export interface AlterTableBatch { - schema: string - table: string - columnOperations: AlterColumnOperation[] - constraintOperations: AlterConstraintOperation[] - indexOperations: AlterIndexOperation[] - renameTable?: string - setSchema?: string - comment?: string | null -} - -// Result types -export interface DDLResult { - success: boolean - executedSql: string[] - errors?: string[] -} - -// Metadata for UI -export interface SequenceInfo { - schema: string - name: string - dataType: string - startValue: string - increment: string -} - -export interface CustomTypeInfo { - schema: string - name: string - type: 'enum' | 'composite' | 'range' | 'domain' - values?: string[] // For enums -} -``` - ---- - -## Phase 2: DDL SQL Builder - -### New File: `apps/desktop/src/main/ddl-builder.ts` - -Create a dedicated builder for DDL statements: - -```typescript -// Key functions to implement: -export function buildCreateTable(definition: TableDefinition): ParameterizedQuery -export function buildAlterTable(batch: AlterTableBatch): ParameterizedQuery[] -export function buildDropTable(schema: string, table: string, cascade?: boolean): ParameterizedQuery -export function buildCreateIndex(schema: string, table: string, index: IndexDefinition): ParameterizedQuery -export function buildPreviewDDL(definition: TableDefinition): string // For UI preview -export function buildAlterPreviewDDL(batch: AlterTableBatch): string[] -``` - -Key considerations: -- Quote all identifiers properly -- Handle reserved words -- Escape string values in defaults -- Support all PostgreSQL constraint syntax -- Handle partial indexes with WHERE -- Support expression indexes - ---- - -## Phase 3: IPC Handlers - -### File: `apps/desktop/src/main/index.ts` - -Add new IPC handlers: - -```typescript -// Create table -ipcMain.handle('db:create-table', async (_, { config, definition }) => { - // Execute CREATE TABLE statement - // Return DDLResult -}) - -// Alter table -ipcMain.handle('db:alter-table', async (_, { config, batch }) => { - // Execute ALTER TABLE statements in transaction - // Return DDLResult -}) - -// Drop table -ipcMain.handle('db:drop-table', async (_, { config, schema, table, cascade }) => { - // Execute DROP TABLE - // Return DDLResult -}) - -// Get table DDL (reverse engineer) -ipcMain.handle('db:get-table-ddl', async (_, { config, schema, table }) => { - // Query pg_catalog to reconstruct CREATE TABLE - // Return TableDefinition -}) - -// Get available sequences -ipcMain.handle('db:get-sequences', async (_, config) => { - // Query pg_sequences - // Return SequenceInfo[] -}) - -// Get custom types -ipcMain.handle('db:get-types', async (_, config) => { - // Query pg_type for enums, composites, etc. - // Return CustomTypeInfo[] -}) - -// Preview DDL without executing -ipcMain.handle('db:preview-ddl', async (_, { definition }) => { - // Return generated SQL string -}) -``` - -### File: `apps/desktop/src/preload/index.ts` - -Update API exposure: - -```typescript -const api = { - // ... existing - ddl: { - createTable: (config, definition) => ipcRenderer.invoke('db:create-table', { config, definition }), - alterTable: (config, batch) => ipcRenderer.invoke('db:alter-table', { config, batch }), - dropTable: (config, schema, table, cascade) => - ipcRenderer.invoke('db:drop-table', { config, schema, table, cascade }), - getTableDDL: (config, schema, table) => - ipcRenderer.invoke('db:get-table-ddl', { config, schema, table }), - getSequences: (config) => ipcRenderer.invoke('db:get-sequences', config), - getTypes: (config) => ipcRenderer.invoke('db:get-types', config), - previewDDL: (definition) => ipcRenderer.invoke('db:preview-ddl', { definition }) - } -} -``` - ---- - -## Phase 4: Tab Store Extension - -### File: `apps/desktop/src/renderer/src/stores/tab-store.ts` - -Add new tab type: - -```typescript -export type TabType = 'query' | 'table-preview' | 'erd' | 'table-designer' - -export interface TableDesignerTab extends BaseTab { - type: 'table-designer' - schemaName: string - tableName?: string // undefined for new table - mode: 'create' | 'edit' - isDirty: boolean -} - -// New actions -createTableDesignerTab: (connectionId: string, schemaName: string, tableName?: string) => string -``` - ---- - -## Phase 5: DDL Store - -### New File: `apps/desktop/src/renderer/src/stores/ddl-store.ts` - -Create Zustand store for table designer state: - -```typescript -interface DDLState { - // Per-tab state (keyed by tabId) - tabState: Map - - // Actions - initializeTab: (tabId: string, mode: 'create' | 'edit', definition?: TableDefinition) => void - - // Column operations - addColumn: (tabId: string, column?: Partial) => void - updateColumn: (tabId: string, columnId: string, updates: Partial) => void - removeColumn: (tabId: string, columnId: string) => void - reorderColumns: (tabId: string, startIndex: number, endIndex: number) => void - - // Constraint operations - addConstraint: (tabId: string, constraint: ConstraintDefinition) => void - updateConstraint: (tabId: string, constraintId: string, updates: Partial) => void - removeConstraint: (tabId: string, constraintId: string) => void - - // Index operations - addIndex: (tabId: string, index: IndexDefinition) => void - updateIndex: (tabId: string, indexId: string, updates: Partial) => void - removeIndex: (tabId: string, indexId: string) => void - - // Table properties - updateTableProperties: (tabId: string, updates: Partial) => void - - // Diff tracking for edit mode - getAlterTableBatch: (tabId: string) => AlterTableBatch | null - - // Validation - validateDefinition: (tabId: string) => { valid: boolean; errors: string[] } - - // Cleanup - clearTabState: (tabId: string) => void -} - -interface TableDesignerState { - original?: TableDefinition // For edit mode - track original state - current: TableDefinition - isDirty: boolean - validationErrors: string[] -} -``` - ---- - -## Phase 6: UI Components - -### New Components Structure: - -``` -src/renderer/src/components/ -├── table-designer/ -│ ├── table-designer.tsx # Main container -│ ├── column-list.tsx # Column grid with drag-drop -│ ├── column-editor-row.tsx # Inline column editing -│ ├── column-editor-dialog.tsx # Full column editor modal -│ ├── constraint-list.tsx # Constraint management -│ ├── constraint-editor-dialog.tsx # Add/edit constraint -│ ├── index-list.tsx # Index management -│ ├── index-editor-dialog.tsx # Add/edit index -│ ├── table-properties.tsx # Table-level settings -│ ├── ddl-preview-panel.tsx # Live SQL preview -│ ├── data-type-select.tsx # Type picker with search -│ └── sequence-select.tsx # Sequence picker -``` - -### Main Component: `table-designer.tsx` - -Layout: -``` -┌─────────────────────────────────────────────────────────────────┐ -│ Toolbar: [Save] [Preview SQL] [Cancel] | Table: schema.name │ -├─────────────────────────────────────────────────────────────────┤ -│ Tabs: [Columns] [Constraints] [Indexes] [Properties] │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ Column Grid (primary view) │ │ -│ │ ┌─────┬──────────┬──────────┬────┬────┬────┬─────────┐ │ │ -│ │ │ ⋮⋮ │ Name │ Type │ PK │ NN │ UQ │ Default │ │ │ -│ │ ├─────┼──────────┼──────────┼────┼────┼────┼─────────┤ │ │ -│ │ │ ⋮⋮ │ id │ uuid │ ✓ │ ✓ │ │ gen_... │ │ │ -│ │ │ ⋮⋮ │ name │ varchar │ │ ✓ │ │ │ │ │ -│ │ │ ⋮⋮ │ email │ varchar │ │ ✓ │ ✓ │ │ │ │ -│ │ │ ⋮⋮ │ created │ timestamp│ │ ✓ │ │ now() │ │ │ -│ │ └─────┴──────────┴──────────┴────┴────┴────┴─────────┘ │ │ -│ │ [+ Add Column] │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ DDL Preview (collapsible) │ │ -│ │ CREATE TABLE schema.table ( │ │ -│ │ id uuid PRIMARY KEY DEFAULT gen_random_uuid(), │ │ -│ │ ... │ │ -│ │ ); │ │ -│ └──────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### Features by Tab: - -**Columns Tab:** -- Drag-drop reordering -- Inline editing for quick changes -- Click to expand for advanced options -- Visual indicators: PK (key icon), NOT NULL (*), UNIQUE (U) -- Type suggestions as you type -- Array type toggle -- Collation selector for text types - -**Constraints Tab:** -- List view with constraint type badges -- Add constraint button with type selector -- Foreign key builder with table/column pickers -- Check constraint with SQL editor -- Unique constraint with column multi-select - -**Indexes Tab:** -- List of indexes with method badges -- Column selector with order (ASC/DESC) -- Partial index condition editor -- INCLUDE column selector -- CONCURRENT option toggle - -**Properties Tab:** -- Table comment -- Schema selector (for changing schema) -- Tablespace selector -- Partition configuration -- Inheritance configuration -- UNLOGGED toggle - ---- - -## Phase 7: Integration Points - -### Schema Explorer Updates - -Add to `schema-explorer.tsx`: - -1. "Create Table" button in schema header: -```tsx - -``` - -2. Context menu on tables: -```tsx - - createTableDesignerTab(connectionId, schema.name, table.name)}> - Edit Table Structure - - handleDropTable(schema.name, table.name)}> - Drop Table... - - -``` - -### Tab Container Updates - -Add to `tab-container.tsx`: - -```tsx -case 'table-designer': - return -``` - ---- - -## Phase 8: Implementation Order - -### Step 1: Foundation (Day 1-2) -1. Add all shared types to `packages/shared/src/index.ts` -2. Create `ddl-builder.ts` with CREATE TABLE generation -3. Add basic IPC handlers for create-table and preview-ddl - -### Step 2: Basic UI (Day 2-3) -1. Create DDL store -2. Add new tab type to tab-store -3. Create basic TableDesigner component with column list -4. Add column editing (inline + dialog) -5. Add DDL preview panel - -### Step 3: Column Features (Day 3-4) -1. Implement all column properties -2. Add data type selector with all PostgreSQL types -3. Add sequence picker for defaults -4. Implement column validation - -### Step 4: Constraints (Day 4-5) -1. Add constraint list UI -2. Implement PRIMARY KEY constraint -3. Implement FOREIGN KEY with table/column picker -4. Implement UNIQUE and CHECK constraints - -### Step 5: Indexes (Day 5) -1. Add index list UI -2. Implement index creation with all options -3. Add partial index support - -### Step 6: ALTER TABLE (Day 6-7) -1. Implement `db:get-table-ddl` handler (reverse engineer table) -2. Implement diff tracking in DDL store -3. Generate ALTER TABLE statements -4. Handle column type changes with USING - -### Step 7: Advanced Features (Day 7-8) -1. Add partitioning support -2. Add inheritance support -3. Add custom type support -4. Add schema explorer integration (create button, context menu) - -### Step 8: Polish (Day 8) -1. Validation and error handling -2. Keyboard shortcuts -3. Undo/redo support -4. Test with various edge cases - ---- - -## Keyboard Shortcuts - -| Shortcut | Action | -|----------|--------| -| `Cmd+S` | Save/Execute DDL | -| `Cmd+Enter` | Preview SQL | -| `Cmd+N` | Add new column | -| `Delete` | Remove selected column | -| `Cmd+Z` | Undo last change | -| `Cmd+Shift+Z` | Redo | -| `Tab` | Move to next column field | -| `Shift+Tab` | Move to previous field | - ---- - -## Validation Rules - -1. **Table name**: Required, valid identifier, not reserved word -2. **Column names**: Required, unique within table, valid identifier -3. **Primary key**: At most one (can be composite) -4. **Foreign keys**: Referenced table must exist, column types must match -5. **Check constraints**: Valid SQL expression -6. **Default values**: Type-compatible with column - ---- - -## Error Handling - -1. **Pre-execution validation**: Show errors inline before attempting execution -2. **Execution errors**: Display PostgreSQL error message clearly -3. **Partial success**: If ALTER TABLE fails mid-batch, show which operations succeeded -4. **Recovery**: Provide option to rollback or continue - ---- - -## Testing Scenarios - -1. Create simple table with various column types -2. Create table with composite primary key -3. Create table with foreign keys to multiple tables -4. Create table with CHECK constraints -5. Create table with partial indexes -6. Edit existing table - add column -7. Edit existing table - change column type -8. Edit existing table - add/remove constraints -9. Drop table with cascade -10. Create table with partitioning diff --git a/docs/backend-plan.md b/docs/backend-plan.md deleted file mode 100644 index f39c554..0000000 --- a/docs/backend-plan.md +++ /dev/null @@ -1,511 +0,0 @@ -# data-peek Backend & Business Plan - -> Planning document for the data-peek licensing system, payment integration, and marketing site. - ---- - -## Business Model - -### Pricing Strategy - -| Tier | Price | What's Included | -|------|-------|-----------------| -| **Pro License** | ~~$99~~ **$29** (Early Bird) | Perpetual license, 1 year of updates, 3 device activations | -| **Free Tier** | $0 | Limited features (see below) | -| **Cloud** (Future) | ~$5-8/month | Connection sync, saved queries, team features | - -### Early Bird Promotion -- **Regular Price:** $99 -- **Launch Price:** $29 (Dec 2024 / Early Adopters) -- **Messaging:** "70% off for early supporters" - -### Free vs Pro Features - -| Feature | Free | Pro ($29) | -|---------|------|-----------| -| Connections | 2 | Unlimited | -| Query History | 50 queries | Unlimited | -| Editor Tabs | 3 | Unlimited | -| Export CSV/JSON | Yes | Yes | -| ER Diagrams | 1 schema | Unlimited | -| Inline Editing | View only | Full CRUD | -| Query Execution Plans | No | Yes | -| Updates | No | 1 year | -| Device Activations | 1 | 3 | - ---- - -## Tech Stack - -| Component | Technology | Why | -|-----------|------------|-----| -| Marketing Site | Next.js 14 (App Router) | SSR, API routes, great DX | -| Database | PostgreSQL (Supabase or Neon) | Familiar, reliable | -| Auth | NextAuth.js | Easy OAuth + credentials | -| Payments | DodoPayments | MoR, global reach, one-time + subscriptions | -| Email | Resend | Developer-friendly, good deliverability | -| Hosting | Vercel | Seamless Next.js deployment | -| File Storage | Cloudflare R2 or S3 | App binary downloads | -| Analytics | Plausible or PostHog | Privacy-friendly | - ---- - -## DodoPayments Integration - -### Why DodoPayments -- Merchant of Record (handles taxes, VAT globally) -- 150+ countries, 80+ currencies -- One-time payments support (perfect for perpetual licenses) -- SDKs for TypeScript/Node.js -- Webhook support for automation - -### Product Setup in DodoPayments -``` -Product: data-peek Pro License -Type: One-time payment -Price: $29 (promo) / $99 (regular) -Metadata: - - license_type: "pro" - - updates_duration: "1_year" - - max_activations: 3 -``` - -### Webhook Events to Handle -| Event | Action | -|-------|--------| -| `payment.completed` | Generate license key, store in DB, send email | -| `payment.refunded` | Revoke license, update status | -| `payment.failed` | Log for debugging, notify if needed | - ---- - -## Database Schema - -### Tables - -```sql --- Customers (synced from DodoPayments) -CREATE TABLE customers ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - email TEXT UNIQUE NOT NULL, - name TEXT, - dodo_customer_id TEXT UNIQUE, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- Licenses -CREATE TABLE licenses ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - customer_id UUID REFERENCES customers(id), - license_key TEXT UNIQUE NOT NULL, - plan TEXT NOT NULL DEFAULT 'pro', - status TEXT NOT NULL DEFAULT 'active', -- active, revoked, expired - max_activations INT NOT NULL DEFAULT 3, - dodo_payment_id TEXT UNIQUE, - dodo_product_id TEXT, - purchased_at TIMESTAMPTZ DEFAULT NOW(), - updates_until TIMESTAMPTZ NOT NULL, -- purchased_at + 1 year - created_at TIMESTAMPTZ DEFAULT NOW() -); - --- Device Activations -CREATE TABLE activations ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - license_id UUID REFERENCES licenses(id) ON DELETE CASCADE, - device_id TEXT NOT NULL, -- Hardware fingerprint - device_name TEXT, -- e.g., "MacBook Pro M2" - os TEXT, -- macos, windows, linux - app_version TEXT, - activated_at TIMESTAMPTZ DEFAULT NOW(), - last_validated_at TIMESTAMPTZ DEFAULT NOW(), - is_active BOOLEAN DEFAULT TRUE, - UNIQUE(license_id, device_id) -); - --- App Releases (for update checks) -CREATE TABLE releases ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - version TEXT UNIQUE NOT NULL, -- semver: 1.0.0 - release_notes TEXT, - download_url_mac TEXT, - download_url_mac_arm TEXT, - download_url_windows TEXT, - download_url_linux TEXT, - is_latest BOOLEAN DEFAULT FALSE, - min_supported_version TEXT, -- for forced updates - released_at TIMESTAMPTZ DEFAULT NOW() -); - --- Indexes -CREATE INDEX idx_licenses_customer ON licenses(customer_id); -CREATE INDEX idx_licenses_key ON licenses(license_key); -CREATE INDEX idx_activations_license ON activations(license_id); -CREATE INDEX idx_activations_device ON activations(device_id); -``` - ---- - -## API Endpoints - -### Public API (called by Electron app) - -``` -POST /api/license/validate - Request: { licenseKey, deviceId } - Response: { valid, plan, status, updatesUntil, activationsRemaining } - -POST /api/license/activate - Request: { licenseKey, deviceId, deviceName, os, appVersion } - Response: { success, activation, license } - -POST /api/license/deactivate - Request: { licenseKey, deviceId } - Response: { success, activationsRemaining } - -GET /api/updates/check?version=1.0.0&platform=macos - Response: { hasUpdate, latestVersion, downloadUrl, releaseNotes, forceUpdate } -``` - -### Webhook Endpoint - -``` -POST /api/webhooks/dodo - - Verify signature from DodoPayments - - Handle payment.completed → create license - - Handle payment.refunded → revoke license -``` - -### Protected API (requires auth, for account dashboard) - -``` -GET /api/account/licenses # List user's licenses -GET /api/account/activations # List active devices -POST /api/account/deactivate # Deactivate a device remotely -GET /api/account/downloads # Get download links -``` - ---- - -## License Key Generation - -### Format -``` -DPRO-XXXX-XXXX-XXXX-XXXX -``` -- Prefix: `DPRO` (data-peek pro) -- 4 groups of 4 alphanumeric characters -- Total: 20 characters (easy to type, hard to guess) - -### Generation Logic -```typescript -import { randomBytes } from 'crypto' - -function generateLicenseKey(): string { - const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' // No 0, O, 1, I (avoid confusion) - const segments: string[] = [] - - for (let i = 0; i < 4; i++) { - let segment = '' - for (let j = 0; j < 4; j++) { - const randomIndex = randomBytes(1)[0] % chars.length - segment += chars[randomIndex] - } - segments.push(segment) - } - - return `DPRO-${segments.join('-')}` -} -``` - ---- - -## Electron App Integration - -### License Activation Flow - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ FIRST LAUNCH │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │ -│ │ Show │ │ User enters │ │ Call /activate │ │ -│ │ License │───▶│ license key │───▶│ API endpoint │ │ -│ │ Dialog │ │ │ │ │ │ -│ └─────────────┘ └──────────────┘ └────────┬────────┘ │ -│ │ │ -│ ┌────────────────────────┴──────┐ │ -│ ▼ ▼ │ -│ ┌─────────────┐ ┌───────────┐ │ -│ │ Valid │ │ Invalid │ │ -│ │ License │ │ Show │ │ -│ │ Store │ │ Error │ │ -│ │ locally │ │ │ │ -│ └─────────────┘ └───────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### Validation Strategy - -```typescript -// On app startup -async function checkLicense(): Promise { - const stored = await getLicenseFromStore() - - if (!stored) { - return { status: 'free', features: FREE_FEATURES } - } - - // Try online validation - try { - const result = await validateOnline(stored.key, getDeviceId()) - - if (result.valid) { - // Update local cache - await updateLicenseCache(result) - return { status: 'pro', features: PRO_FEATURES, license: result } - } else { - // License revoked or invalid - await clearLicenseStore() - return { status: 'free', features: FREE_FEATURES, error: result.reason } - } - } catch (error) { - // Offline - use cached validation (grace period) - if (stored.cachedValidation && isWithinGracePeriod(stored.lastValidated)) { - return { status: 'pro', features: PRO_FEATURES, offline: true } - } - return { status: 'free', features: FREE_FEATURES } - } -} -``` - -### Device Fingerprinting - -```typescript -import { machineIdSync } from 'node-machine-id' -import os from 'os' - -function getDeviceId(): string { - return machineIdSync() // Unique per machine -} - -function getDeviceName(): string { - return os.hostname() -} - -function getDeviceInfo() { - return { - deviceId: getDeviceId(), - deviceName: getDeviceName(), - os: process.platform, // darwin, win32, linux - appVersion: app.getVersion() - } -} -``` - ---- - -## Marketing Site Structure - -### Pages - -``` -/ # Landing page -/pricing # Pricing with buy button -/download # Platform-specific downloads -/docs # Getting started guide -/docs/[slug] # Individual doc pages -/faq # Frequently asked questions -/changelog # Release history -/blog # Updates, tutorials (future) -/account # Dashboard (protected) -/account/licenses # Manage licenses (protected) -/account/downloads # Download links (protected) -/login # Auth page -``` - -### Landing Page Sections - -1. **Hero** - Tagline, screenshot, CTA button -2. **Problem** - Pain points with existing tools -3. **Features** - Key capabilities with visuals -4. **Demo** - GIF or video of the app in action -5. **Comparison** - vs pgAdmin, DBeaver, TablePlus -6. **Pricing** - Single tier, early bird callout -7. **FAQ** - Common questions -8. **CTA** - Final call to action - ---- - -## Email Templates - -### Welcome Email (after purchase) -``` -Subject: Your data-peek Pro license 🎉 - -Hi {name}, - -Thank you for purchasing data-peek Pro! - -Your license key: {license_key} - -Quick start: -1. Download data-peek: {download_link} -2. Open the app and go to Settings → License -3. Enter your license key - -Your license includes: -✓ 1 year of updates (until {updates_until}) -✓ 3 device activations -✓ All Pro features unlocked - -Need help? Reply to this email. - -Happy querying! -— The data-peek team -``` - -### License Activation Email -``` -Subject: data-peek activated on {device_name} - -Your license was just activated on a new device: - -Device: {device_name} -Activated: {timestamp} -Activations used: {used}/{max} - -Not you? Manage your devices at {dashboard_link} -``` - ---- - -## Implementation Roadmap - -### Phase 1: MVP (Week 1) -- [ ] Set up Next.js project with App Router -- [ ] Configure DodoPayments account and product -- [ ] Create database schema (Supabase/Neon) -- [ ] Implement `/api/webhooks/dodo` endpoint -- [ ] Implement `/api/license/validate` endpoint -- [ ] Implement `/api/license/activate` endpoint -- [ ] Build landing page with pricing -- [ ] Build download page -- [ ] Deploy to Vercel -- [ ] Test end-to-end purchase flow - -### Phase 2: Polish (Week 2) -- [ ] Add account dashboard with NextAuth -- [ ] Implement device management UI -- [ ] Add email notifications (Resend) -- [ ] Implement update checker API -- [ ] Add analytics (Plausible) -- [ ] Write FAQ page -- [ ] Add basic docs/getting started - -### Phase 3: Electron Integration (Week 2-3) -- [ ] Add license dialog to data-peek app -- [ ] Implement device fingerprinting -- [ ] Add license validation on startup -- [ ] Implement feature gating (free vs pro) -- [ ] Add "Check for updates" functionality -- [ ] Handle offline grace period -- [ ] Test activation/deactivation flow - -### Phase 4: Launch Prep (Week 3) -- [ ] Create app store screenshots -- [ ] Record demo video/GIF -- [ ] Write launch blog post -- [ ] Set up social media accounts -- [ ] Prepare ProductHunt launch -- [ ] Build releases for all platforms -- [ ] Final QA testing - ---- - -## Security Considerations - -### API Security -- [ ] Rate limiting on all endpoints (prevent brute force) -- [ ] Webhook signature verification (DodoPayments) -- [ ] Input validation on all parameters -- [ ] SQL injection prevention (parameterized queries) - -### License Security -- [ ] License keys stored hashed in DB? (or encrypted) -- [ ] Device ID cannot be spoofed easily -- [ ] Offline grace period limited (7-14 days) -- [ ] Anomaly detection (too many activations) - -### Data Privacy -- [ ] Minimal data collection -- [ ] No tracking in the app -- [ ] Clear privacy policy -- [ ] GDPR compliance (if EU customers) - ---- - -## Metrics to Track - -### Business Metrics -- Total licenses sold -- Revenue (MRR if cloud tier added) -- Conversion rate (free → pro) -- Refund rate - -### Product Metrics -- Daily/weekly active users -- Feature usage (which features are popular) -- Error rates -- Update adoption rate - -### Marketing Metrics -- Website visitors -- Download counts -- Trial starts -- Email open/click rates - ---- - -## Future Considerations - -### Cloud Tier (v2) -- User accounts required -- Connection sync across devices -- Saved queries library -- Team sharing features -- Subscription billing via DodoPayments - -### Additional Payment Options -- Regional pricing -- Team/volume licenses -- Educational discounts -- Lifetime deals (AppSumo?) - ---- - -## Open Questions - -- [ ] Exact DodoPayments webhook event names (check their docs) -- [ ] License key format preference -- [ ] Offline grace period duration (7 or 14 days?) -- [ ] Domain name for marketing site -- [ ] App signing certificates (macOS notarization, Windows signing) - ---- - -## Resources - -- [DodoPayments Docs](https://docs.dodopayments.com) -- [Next.js App Router](https://nextjs.org/docs/app) -- [NextAuth.js](https://next-auth.js.org/) -- [Resend](https://resend.com/docs) -- [Supabase](https://supabase.com/docs) -- [node-machine-id](https://www.npmjs.com/package/node-machine-id) - ---- - -*Document created: November 2024* -*Last updated: November 2024* diff --git a/docs/features.md b/docs/features.md deleted file mode 100644 index f170097..0000000 --- a/docs/features.md +++ /dev/null @@ -1,279 +0,0 @@ -# data-peek Feature Overview - -> A minimal, fast, beautiful PostgreSQL client for developers who want to quickly peek at their data. - ---- - -## Product Summary - -**data-peek** is a lightweight desktop database client designed for developers who need quick, frictionless access to their PostgreSQL databases. Unlike bloated alternatives like pgAdmin or DBeaver, data-peek focuses on speed, simplicity, and a keyboard-first experience. - -**Target Audience:** Developers, data engineers, backend engineers, and anyone who needs to quickly query and explore PostgreSQL databases without the overhead of enterprise tools. - -**Platforms:** macOS (Apple Silicon + Intel), Windows, Linux - ---- - -## Key Value Propositions - -| Benefit | Description | -|---------|-------------| -| **Lightning Fast** | Opens in under 2 seconds. No splash screens, no waiting. | -| **Zero Configuration** | Connect and query immediately. No complex setup required. | -| **Keyboard-First** | Power users can do everything without touching the mouse. | -| **Beautiful & Modern** | Dark and light themes with a clean, distraction-free UI. | -| **Privacy-First** | No telemetry, no tracking. Your data stays on your machine. | -| **Secure** | Connection credentials are encrypted locally. | -| **Pay Once, Own Forever** | No subscriptions. One-time purchase with 1 year of updates. | - ---- - -## Pricing - -### Free Tier -Get started at no cost: -- 2 database connections -- 50 query history items -- 3 editor tabs -- 1 schema for ER diagrams -- CSV/JSON export - -### Pro License — ~~$99~~ $29 (Early Bird) -Unlock everything: -- **Unlimited** connections -- **Unlimited** query history -- **Unlimited** tabs -- **Unlimited** ER diagrams -- Inline data editing (INSERT/UPDATE/DELETE) -- Query execution plans (EXPLAIN/ANALYZE) -- 3 device activations -- 1 year of updates -- **Pay once, use forever** - -### Cloud (Coming Soon) -For power users and teams: -- Everything in Pro -- Sync connections across devices -- Cloud-saved queries -- Team sharing -- ~$5-8/month - ---- - -## Feature List - -### Connection Management - -| Feature | Description | -|---------|-------------| -| **Quick Connection Setup** | Add connections with host, port, database, user, and password — or paste a connection string | -| **Connection String Parsing** | Paste any PostgreSQL connection URL and auto-fill all fields | -| **Test Before Save** | Verify connections work before adding them | -| **Encrypted Storage** | Credentials stored securely with encryption | -| **SSL Support** | Connect to SSL-enabled databases | -| **Connection Switcher** | Quickly switch between multiple database connections | -| **Edit & Delete** | Manage saved connections with ease | - -### Query Editor - -| Feature | Description | -|---------|-------------| -| **Monaco Editor** | Same editor engine that powers VS Code | -| **SQL Syntax Highlighting** | Full SQL syntax highlighting with PostgreSQL support | -| **Smart Autocomplete** | Schema-aware suggestions for tables, columns, and SQL keywords | -| **Multi-Tab Support** | Work on multiple queries simultaneously with independent tabs | -| **Query Formatting** | Auto-format SQL with `Cmd/Ctrl + Shift + F` | -| **Run Query** | Execute with `Cmd/Ctrl + Enter` | -| **Collapsible Editor** | Minimize the editor to focus on results | - -### Results Viewer - -| Feature | Description | -|---------|-------------| -| **Data Table View** | View results in a clean, sortable table | -| **Data Type Indicators** | Color-coded badges showing column types | -| **Query Metrics** | See row count and query execution time | -| **Pagination** | Navigate large result sets with customizable page sizes | -| **Copy Cell** | Click any cell to copy its value | -| **Copy Row as JSON** | Export individual rows as JSON objects | -| **Export to CSV** | Download results as CSV files | -| **Export to JSON** | Download results as JSON files | -| **NULL Styling** | Clear visual distinction for NULL values | -| **Foreign Key Navigation** | Click FK cells to view related records | -| **JSON/JSONB Viewer** | Expand and inspect JSON columns inline | - -### Schema Explorer - -| Feature | Description | -|---------|-------------| -| **Tree View Navigation** | Browse schemas, tables, and views hierarchically | -| **Column Details** | See column names, data types, and constraints | -| **Primary Key Indicators** | Visual markers for primary key columns | -| **Nullable Indicators** | See which columns allow NULL values | -| **Foreign Key Display** | View foreign key relationships | -| **Table Search** | Filter tables by name with instant search | -| **Click to Query** | Click any table to generate a SELECT query | -| **Schema Refresh** | Reload schema after database changes | - -### Query History - -| Feature | Description | -|---------|-------------| -| **Auto-Save** | Every executed query is automatically saved | -| **Persistent Storage** | History survives app restarts | -| **Query Metadata** | See execution time, row count, and status for each query | -| **Quick Load** | Click any history item to load it into the editor | -| **Copy to Clipboard** | Copy previous queries without loading | -| **Clear History** | Remove all or individual history items | -| **Query Type Badges** | Visual indicators for SELECT, INSERT, UPDATE, DELETE | -| **Relative Timestamps** | "5 minutes ago", "yesterday", etc. | - -### Inline Data Editing - -| Feature | Description | -|---------|-------------| -| **Edit Cells** | Double-click to modify cell values directly | -| **Add Rows** | Insert new records with a visual form | -| **Delete Rows** | Remove records with confirmation | -| **SQL Preview** | Review generated SQL before executing changes | -| **Batch Operations** | Queue multiple changes before committing | -| **Discard Changes** | Undo pending edits before saving | -| **Type-Safe Editing** | Input validation based on column data types | - -### ER Diagram Visualization - -| Feature | Description | -|---------|-------------| -| **Visual Schema Map** | See your database structure as an interactive diagram | -| **Table Nodes** | Each table displays all columns with types | -| **Relationship Lines** | Foreign key connections visualized as links | -| **Primary Key Highlights** | Yellow indicators for PK columns | -| **Foreign Key Highlights** | Blue indicators for FK columns | -| **Pan & Zoom** | Navigate large schemas with ease | -| **Mini Map** | Overview navigation for complex databases | - -### Query Execution Plans - -| Feature | Description | -|---------|-------------| -| **EXPLAIN Visualization** | See query execution plans in a visual tree | -| **Node Type Coloring** | Color-coded operations (scans, joins, sorts) | -| **Cost Analysis** | View estimated vs actual costs | -| **Performance Metrics** | Execution time breakdown by operation | -| **Buffer Statistics** | I/O and memory usage details | -| **Expandable Nodes** | Drill into plan details | - -### User Interface - -| Feature | Description | -|---------|-------------| -| **Dark Mode** | Easy on the eyes for long coding sessions | -| **Light Mode** | Clean, bright interface when you prefer it | -| **System Preference** | Automatically match your OS theme | -| **Resizable Panels** | Drag to resize sidebar and editor | -| **Collapsible Sidebar** | Maximize workspace when needed | -| **Loading States** | Clear feedback during operations | -| **Error Handling** | Helpful error messages with details | -| **Empty States** | Guided prompts when there's no data | - -### Keyboard Shortcuts - -| Shortcut | Action | -|----------|--------| -| `Cmd/Ctrl + Enter` | Execute query | -| `Cmd/Ctrl + Shift + F` | Format SQL | -| `Cmd/Ctrl + P` | Open connection picker | -| `Cmd/Ctrl + S` | Save query to file | -| `Cmd/Ctrl + O` | Open query from file | -| `Cmd/Ctrl + Shift + 1-9` | Switch between connections | - ---- - -## Technical Highlights - -| Aspect | Details | -|--------|---------| -| **Framework** | Electron with React 19 | -| **Editor** | Monaco (VS Code engine) | -| **Database Driver** | Native PostgreSQL (pg) | -| **Local Storage** | SQLite for history and settings | -| **Security** | Encrypted credential storage | -| **Build Targets** | macOS DMG, Windows exe/msi, Linux AppImage | - ---- - -## What data-peek is NOT - -To set clear expectations: - -- **Not a database admin tool** — Focus is on querying and exploring, not server management -- **Not a data migration tool** — No import/export of entire databases -- **Not multi-database** — PostgreSQL only (MySQL/SQLite coming in future versions) -- **Not enterprise software** — Built for individual developers (team features coming with Cloud tier) - ---- - -## Comparison with Alternatives - -| Feature | data-peek | pgAdmin | DBeaver | TablePlus | -|---------|-----------|---------|---------|-----------| -| Startup Time | < 2s | 5-10s | 10-15s | 2-3s | -| Memory Usage | Low | High | Very High | Low | -| Learning Curve | Minimal | Steep | Steep | Minimal | -| Price | Free + $29 Pro | Free | Free/Paid | $69 | -| PostgreSQL Focus | Yes | Yes | No | No | -| ER Diagrams | Yes | Yes | Yes | Yes | -| Inline Editing | Yes | Yes | Yes | Yes | -| Query Plans | Yes | Yes | Yes | Limited | -| Modern UI | Yes | No | No | Yes | - ---- - -## Coming Soon - -Features planned for future releases: - -- MySQL and SQLite support -- SSH tunnel connections -- Saved queries / snippets library -- Query cancellation -- CSV data import -- Connection groups/folders -- **Cloud Sync** — Sync connections and saved queries across devices -- **Team Features** — Share queries and connections with your team - ---- - -## Screenshots - -*[Add screenshots here]* - -- Connection dialog -- Query editor with results -- Schema explorer tree -- ER diagram view -- Query execution plan -- Inline data editing -- Dark/Light theme comparison - ---- - -## One-Liner Descriptions - -For various marketing contexts: - -**Tagline:** -> Peek at your data. Fast. - -**Short (10 words):** -> A fast, beautiful PostgreSQL client for developers who value simplicity. - -**Medium (25 words):** -> data-peek is a lightweight PostgreSQL desktop client with a modern UI, keyboard shortcuts, and features like ER diagrams and query plans — without the bloat. - -**Long (50 words):** -> data-peek is the PostgreSQL client developers actually want to use. Lightning-fast startup, Monaco-powered SQL editor, visual ER diagrams, query execution plans, inline data editing, and a beautiful dark/light UI. No telemetry, no subscriptions, no bloat. Pay once, own forever. Available for macOS, Windows, and Linux. - ---- - -*Document generated: November 2025* diff --git a/docs/future-plan.md b/docs/future-plan.md deleted file mode 100644 index 513f934..0000000 --- a/docs/future-plan.md +++ /dev/null @@ -1,20 +0,0 @@ -# Future Plan - -## Quick Wins -- [x] Query history - browse and re-run previous queries -- [x] Export results to CSV/JSON -- [x] Table search - quickly find tables in large schemas -- [x] Dark/light theme toggle (already in settings) -- [x] Connection picker (Cmd+P) and quick switching (Cmd+Shift+1-9) - -## Medium Effort -- [ ] Saved queries/snippets - bookmark frequently used queries -- [ ] Column statistics - show min/max/null counts/distinct values -- [x] Connection folders/groups - organize many connections -- [ ] Multi-statement query execution (run multiple queries separated by `;`) - -## Bigger Features -- [x] ERD visualization - see table relationships visually -- [x] Query execution plan viewer - analyze query performance (EXPLAIN ANALYZE) -- [ ] Data visualization - charts from query results -- [x] SQL autocomplete improvements (table/column suggestions) - already implemented diff --git a/docs/release-guide.md b/docs/release-guide.md deleted file mode 100644 index 9cb1a2c..0000000 --- a/docs/release-guide.md +++ /dev/null @@ -1,329 +0,0 @@ -# data-peek Release Guide - -This document covers everything needed to ship data-peek for macOS, Windows, and Linux. - ---- - -## TL;DR — Can I Ship for Free? - -**Yes.** Here's the reality: - -| Platform | Free Option | Paid Option | User Experience Difference | -|----------|-------------|-------------|---------------------------| -| **Linux** | ✅ No signing needed | N/A | No difference | -| **macOS** | ✅ Ad-hoc signing | $99/year Apple Developer | Warning dialog vs no warning | -| **Windows** | ✅ Unsigned | ~$200-400/year certificate | SmartScreen warning vs trusted | - -Most open-source Electron apps ship unsigned. Users can bypass warnings. - ---- - -## Platform-Specific Details - -### Linux — No Signing Required ✅ - -Linux doesn't require code signing. Your current setup is ready: -- AppImage: Works out of the box -- Snap: Works out of the box -- Deb: Works out of the box - -**Action needed:** None. Just build and distribute. - ---- - -### macOS — Options - -#### Option 1: Unsigned/Ad-hoc (Free) ✅ - -Users will see: *"data-peek can't be opened because Apple cannot check it for malicious software"* - -**Workaround for users:** -1. Right-click the app → Open (first time only) -2. Or: `xattrs -cr /Applications/data-peek.app` - -**Pros:** Free, works fine for developer tools -**Cons:** Scary warning, extra step for users - -**Current config already supports this** — just build and distribute. - -#### Option 2: Apple Developer Program ($99/year) - -Users will see: No warning, app opens normally. - -**What you get:** -- Developer ID certificate for signing -- Notarization (Apple scans your app) -- No Gatekeeper warnings - -**When to consider:** If you want a polished user experience or plan to distribute via Mac App Store. - ---- - -### Windows — Options - -#### Option 1: Unsigned (Free) ✅ - -Users will see: *"Windows protected your PC — Microsoft Defender SmartScreen prevented an unrecognized app from starting"* - -**Workaround for users:** -1. Click "More info" -2. Click "Run anyway" - -**Pros:** Free -**Cons:** SmartScreen warning scares users, some corporate machines block unsigned apps - -**Note:** SmartScreen builds reputation over time. After enough users download and run your app without issues, warnings may reduce. - -#### Option 2: Code Signing Certificate ($200-600/year) - -Traditional EV (Extended Validation) certificates from: -- DigiCert (~$400-600/year) -- Sectigo (~$200-400/year) -- SSL.com (~$200/year) - -**Pros:** Immediate SmartScreen trust (EV certs), professional appearance -**Cons:** Expensive, annual renewal - -#### Option 3: Azure Trusted Signing (~$10/month) 💡 - -Microsoft's newer, cheaper alternative: -- Pay-as-you-go pricing -- Works with electron-builder -- Builds SmartScreen reputation faster than unsigned - -**Pros:** Cheap, modern, Microsoft-backed -**Cons:** Requires Azure account, slightly more setup - -#### Option 4: SignPath (Free for Open Source) 💡 - -[SignPath.io](https://signpath.io) offers free code signing for open-source projects. - -**Requirements:** -- Public GitHub repository -- OSS license (MIT qualifies) -- Apply and get approved - -**Pros:** Completely free -**Cons:** Approval process, builds must go through their CI - ---- - -## Recommended Approach - -### For Initial Release (v1.0) - -Ship unsigned on all platforms: - -1. **Linux** — No changes needed -2. **macOS** — Users right-click → Open (one time) -3. **Windows** — Users click "More info" → "Run anyway" - -Include clear installation instructions in README. - -### For Future Releases - -Consider signing when: -- You have paying users (Pro tier) -- Download volume justifies the cost -- Corporate users need signed apps - ---- - -## Release Checklist - -### Pre-Release - -- [ ] Update `electron-builder.yml`: - - [ ] Change `appId` to `com.datapeek.app` - - [ ] Remove `electronDownload.mirror` line - - [ ] Update `publish` config (see below) -- [ ] Update `package.json`: - - [ ] Set proper `author` - - [ ] Set `homepage` to GitHub repo -- [ ] Add LICENSE file (MIT) -- [ ] Add README with screenshots and install instructions -- [ ] Test builds on each platform - -### electron-builder.yml Updates - -```yaml -appId: com.datapeek.app -productName: data-peek - -# ... keep existing config ... - -# For GitHub Releases (recommended) -publish: - provider: github - owner: Rohithgilla12 - repo: data-peek - -# Remove this line: -# electronDownload: -# mirror: https://npmmirror.com/mirrors/electron/ -``` - -### Building Releases - -```bash -# From apps/desktop directory - -# macOS (builds for current architecture) -pnpm build:mac - -# macOS universal (Intel + Apple Silicon) -# Add to electron-builder.yml under mac: -# target: -# - target: dmg -# arch: [x64, arm64] - -# Windows -pnpm build:win - -# Linux -pnpm build:linux -``` - -### Creating a GitHub Release - -1. Tag your release: - ```bash - git tag v1.0.0 - git push origin v1.0.0 - ``` - -2. Go to GitHub → Releases → Draft new release - -3. Upload build artifacts: - - `data-peek-desktop-1.0.0.dmg` (macOS) - - `data-peek-desktop-1.0.0-setup.exe` (Windows) - - `data-peek-desktop-1.0.0.AppImage` (Linux) - - `data-peek-desktop-1.0.0.snap` (Linux) - - `data-peek-desktop-1.0.0.deb` (Linux) - -4. Write release notes and publish - ---- - -## CI/CD (Optional but Recommended) - -Automate builds with GitHub Actions. Create `.github/workflows/release.yml`: - -```yaml -name: Release - -on: - push: - tags: - - 'v*' - -jobs: - build: - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v2 - with: - version: 8 - - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: 'pnpm' - - - run: pnpm install - - - name: Build - run: pnpm --filter @data-peek/desktop build - - - name: Build Electron app - run: | - cd apps/desktop - pnpm exec electron-builder --publish never - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: dist-${{ matrix.os }} - path: apps/desktop/dist/*.{dmg,exe,AppImage,snap,deb} -``` - ---- - -## Installation Instructions for Users - -Include this in your README: - -### macOS - -1. Download `data-peek-desktop-x.x.x.dmg` -2. Open the DMG and drag to Applications -3. **First launch:** Right-click the app → Click "Open" → Click "Open" again - - (This is required because the app is not notarized) - -### Windows - -1. Download `data-peek-desktop-x.x.x-setup.exe` -2. Run the installer -3. **If you see SmartScreen warning:** Click "More info" → "Run anyway" - -### Linux - -**AppImage:** -```bash -chmod +x data-peek-desktop-x.x.x.AppImage -./data-peek-desktop-x.x.x.AppImage -``` - -**Snap:** -```bash -sudo snap install data-peek-desktop-x.x.x.snap --dangerous -``` - -**Deb:** -```bash -sudo dpkg -i data-peek-desktop-x.x.x.deb -``` - ---- - -## Cost Summary - -| Approach | Cost | Notes | -|----------|------|-------| -| Ship unsigned | $0 | Users see warnings | -| macOS only signing | $99/year | Apple Developer Program | -| Windows only signing | $10-400/year | Azure TS or traditional cert | -| Both platforms signed | $109-500/year | Combined | -| SignPath (Windows) | $0 | Free for OSS, requires approval | - ---- - -## Future Considerations - -### Auto-Updates - -Current config uses `electron-updater`. For unsigned apps: -- macOS: Auto-updates work but user must approve -- Windows: Auto-updates work -- Linux: AppImage supports auto-updates via `electron-updater` - -### Mac App Store - -Requires Apple Developer Program + additional review process. Consider only if: -- You want discoverability -- You're okay with Apple's 15-30% cut -- Your app meets their guidelines - -### Windows Store - -Possible but requires Microsoft Partner account. Similar considerations to Mac App Store. diff --git a/docs/scope.md b/docs/scope.md deleted file mode 100644 index 4a3dfe9..0000000 --- a/docs/scope.md +++ /dev/null @@ -1,333 +0,0 @@ -# data-peek v1.0 Scope Document - -> A minimal, fast, beautiful Postgres client for developers who want to quickly peek at their data. - -## Target User - -Developers who need a lightweight alternative to pgAdmin/DBeaver for day-to-day queries. - -## Core Principles - -- **Simple over feature-rich** — Do less, but do it well -- **Fast to open, fast to query** — No bloat -- **Keyboard-first** — Power users shouldn't need a mouse - ---- - -## Tech Stack - -| Layer | Choice | Why | -|-------|--------|-----| -| Desktop | Electron | Community, TS-native, contributor-friendly | -| Frontend | React + TypeScript | Industry standard, type safety | -| Bundler | electron-vite | Fast, modern, great DX | -| UI | shadcn/ui + Tailwind | Beautiful, accessible, customizable | -| State | Zustand | Simple, minimal boilerplate | -| Query Editor | Monaco | VS Code engine, excellent SQL support | -| Local Storage | SQLite (better-sqlite3) | Query history, cached schemas | -| Config | electron-store | Encrypted connection credentials | -| Database Client | pg | Native Postgres driver | - ---- - -## In Scope (v1.0) - -### Connection Management - -- [x] Add new Postgres connection (host, port, database, user, password) -- [x] Edit existing connection -- [x] Delete connection -- [x] Test connection before saving -- [x] Encrypted credential storage (electron-store) -- [x] Connection list in sidebar -- [x] SSL connection support (basic) - -### Query Editor - -- [x] Monaco editor with SQL syntax highlighting -- [x] Single query tab (multi-tab is v1.1) — *Actually implemented multi-tab!* -- [x] Run query: `Cmd/Ctrl + Enter` -- [ ] Clear editor: `Cmd/Ctrl + L` -- [x] Basic error display with message - -### Results Viewer - -- [x] Table view with columns and rows -- [x] Column headers with data type indicators -- [x] Row count and query duration display -- [x] Client-side pagination (100 rows per page) -- [x] Copy cell value on click -- [x] Copy row as JSON -- [x] Export results to CSV -- [x] Export results to JSON -- [x] NULL value indicator styling - -### Schema Explorer - -- [x] Tree view: Connection → Schemas → Tables/Views -- [x] Show columns under each table: - - Column name - - Data type - - Nullable indicator - - Primary key indicator -- [x] Click table name to insert into editor -- [x] Refresh schema button -- [x] Collapse/expand nodes - -### Query History - -- [x] Auto-save last 100 executed queries (local SQLite) -- [x] Store: query text, timestamp, duration, row count -- [x] Display in sidebar panel -- [x] Click to load query into editor -- [x] Clear history option -- [x] Persist across sessions - -### UI/UX - -- [x] Dark mode only (light mode is v1.1) — *Actually implemented light mode + system preference!* -- [x] Resizable sidebar (drag handle) -- [x] Loading states for queries -- [x] Empty states with helpful messages -- [x] Error states with clear messaging -- [x] Keyboard shortcuts: - - `Cmd/Ctrl + Enter` — Run query - - `Cmd/Ctrl + S` — Save query to file - - `Cmd/Ctrl + O` — Open query from file - - `Cmd/Ctrl + ,` — Open settings - -### Platform Support - -- [x] macOS build (DMG, Apple Silicon + Intel) -- [x] Linux build (AppImage) -- [x] Windows build (exe/msi) - ---- - -## Out of Scope (v1.0) - -These features are explicitly deferred to future versions. When tempted to add them, resist. - -| Feature | Target Version | Status | -|---------|----------------|--------| -| Multiple query tabs | v1.1 | ✅ Done | -| MySQL adapter | v1.1 | | -| SQLite adapter | v1.1 | | -| Light theme | v1.1 | ✅ Done | -| Connection groups/folders | v1.1 | | -| Autocomplete (tables/columns) | v1.2 | ✅ Done (schema-aware) | -| Query formatting/beautify | v1.2 | ✅ Done | -| Saved queries / snippets library | v1.2 | | -| SSH tunnel connections | v1.2 | | -| Query cancellation | v1.2 | | -| Table data editing (inline UPDATE/INSERT) | v2.0 | ✅ Done | -| ER diagram visualization | v2.0 | ✅ Done | -| Query EXPLAIN/ANALYZE visualizer | v2.0 | ✅ Done | -| Import data from CSV | v2.0 | | -| Database diff tool | v2.0 | | -| Cloud sync (connections, history) | Pro | | -| Team workspaces | Pro | | -| Shared query library | Pro | | -| SSO / SAML | Pro | | -| Audit logs | Pro | | - ---- - -## Technical Boundaries - -- **No ORM** — Raw `pg` client only, keep it simple -- **No server component** — Pure desktop app, no backend -- **No auth in v1** — Local app, no user accounts -- **No auto-updates in v1** — Manual download for updates -- **No telemetry** — Privacy first, no tracking - ---- - -## Architecture Overview - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Renderer Process (React) │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐│ -│ │ Sidebar │ │ Query Editor│ │ Results Viewer ││ -│ │ - Conns │ │ (Monaco) │ │ - Table/JSON/Export ││ -│ │ - Schema │ │ │ │ ││ -│ │ - History │ │ │ │ ││ -│ └─────────────┘ └─────────────┘ └─────────────────────────┘│ -│ │ │ -│ ┌───────▼───────┐ │ -│ │ IPC Bridge │ │ -│ └───────┬───────┘ │ -└────────────────────────────┼────────────────────────────────┘ - │ -┌────────────────────────────┼────────────────────────────────┐ -│ Main Process (Node.js) │ -│ ┌───────▼───────┐ │ -│ │ Service Layer │ │ -│ └───────┬───────┘ │ -│ ┌───────────────────┼───────────────────┐ │ -│ ▼ ▼ ▼ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │Connection│ │ Query │ │ Schema │ │ -│ │ Manager │ │ Engine │ │ Explorer │ │ -│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ -│ └──────────────────┼───────────────────┘ │ -│ ┌─────▼─────┐ │ -│ │ Postgres │ │ -│ │ Adapter │ │ -│ └───────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## UI Layout - -``` -┌──────────────────────────────────────────────────────────────────┐ -│ data-peek [Connection: prod-db ▼] [⚙️] │ -├────────────────┬─────────────────────────────────────────────────┤ -│ │ │ -│ CONNECTIONS │ SELECT * FROM users │ -│ ├─ prod-db ● │ WHERE created_at > '2024-01-01' │ -│ ├─ staging │ LIMIT 100; │ -│ └─ local │ │ -│ │ [▶ Run] │ -│ SCHEMA ├─────────────────────────────────────────────────┤ -│ ▼ public │ │ -│ ├─ users │ id │ name │ email │ created_at │ -│ ├─ orders │ ───┼─────────┼─────────────────┼────────────── │ -│ └─ products │ 1 │ Alice │ alice@test.com │ 2024-01-15 │ -│ │ 2 │ Bob │ bob@test.com │ 2024-02-20 │ -│ HISTORY │ 3 │ Carol │ carol@test.com │ 2024-03-10 │ -│ ├─ SELECT ... │ ... │ -│ ├─ UPDATE ... ├─────────────────────────────────────────────────┤ -│ └─ ... │ ✓ 100 rows │ 24ms │ Page 1/10 │ [CSV] [JSON] │ -└────────────────┴─────────────────────────────────────────────────┘ -``` - ---- - -## Milestones - -### M1: Foundation -- [x] Electron app scaffolded with electron-vite -- [x] Monorepo structure with pnpm workspaces -- [x] Can connect to a Postgres database -- [x] Can run a hardcoded query and log results - -### M2: Connection Management -- [x] Connection form UI (add/edit) -- [x] Connection list in sidebar -- [x] Test connection functionality -- [x] Encrypted storage with electron-store -- [x] Delete connection - -### M3: Schema Explorer -- [x] Fetch and display schemas -- [x] Fetch and display tables per schema -- [x] Fetch and display columns per table -- [x] Tree view component with expand/collapse -- [x] Click to insert table name - -### M4: Query Editor -- [x] Monaco editor integration -- [x] SQL syntax highlighting -- [x] Run query button -- [x] Keyboard shortcut (Cmd+Enter) -- [x] Error display - -### M5: Results Viewer -- [x] Table component for results -- [x] Column headers -- [x] Pagination -- [x] Copy cell/row -- [x] Export to CSV -- [x] Export to JSON - -### M6: Query History -- [x] SQLite setup for local storage -- [x] Auto-save executed queries -- [x] History list in sidebar -- [x] Click to load into editor -- [x] Clear history - -### M7: Polish & Release -- [x] Keyboard shortcuts complete -- [x] Loading states -- [x] Error handling -- [x] Empty states -- [x] Build for macOS -- [x] Build for Linux -- [x] Build for Windows -- [ ] README with screenshots -- [ ] v1.0 release - ---- - -## Success Criteria - -v1.0 is complete when: - -1. ✅ Can connect to a Postgres database -2. ✅ Can browse schema (schemas, tables, columns) -3. ✅ Can write and run SQL queries -4. ✅ Can view results in a table -5. ✅ Can export results to CSV/JSON -6. ✅ Query history persists across sessions -7. ✅ App opens in under 2 seconds -8. ✅ Feels snappy — no UI lag -9. ✅ Builds available for macOS, Linux, Windows - ---- - -## Non-Goals - -To keep scope tight, these are explicitly NOT goals for v1: - -- Being a full database administration tool -- Competing with DataGrip/TablePlus on features -- Supporting every Postgres feature -- Having a plugin system -- Mobile support - ---- - -## Open Source Strategy - -### License -MIT — Maximum adoption, contributor-friendly. - -### Contribution Guidelines -- PRs welcome for bug fixes and v1 scope items -- Features outside v1 scope will be reviewed for v1.1+ -- All PRs require tests for new functionality - -### Future Monetization (Post v1) -Open core model: -- Free: Everything in v1 scope, forever -- Pro: Cloud sync, team features, priority support - ---- - -## Resources - -- [Electron Documentation](https://www.electronjs.org/docs) -- [electron-vite](https://electron-vite.org/) -- [Monaco Editor React](https://github.com/suren-atoyan/monaco-react) -- [shadcn/ui](https://ui.shadcn.com/) -- [Zustand](https://github.com/pmndrs/zustand) -- [node-postgres (pg)](https://node-postgres.com/) - ---- - -## Changelog - -| Date | Change | -|------|--------| -| 2024-XX-XX | Initial scope document created | -| 2025-11-28 | Updated implementation status - v1.0 scope complete + bonus v2.0 features | - ---- - -*Remember: When in doubt, leave it out. Ship v1, then iterate.* \ No newline at end of file diff --git a/docs/web-integration-guide.md b/docs/web-integration-guide.md deleted file mode 100644 index 479c083..0000000 --- a/docs/web-integration-guide.md +++ /dev/null @@ -1,831 +0,0 @@ -# data-peek Web Integration Guide - -> Complete walkthrough for testing, purchasing, and integrating the license system with the desktop app. - ---- - -## Table of Contents - -1. [Feature Checklist](#feature-checklist) -2. [Environment Setup](#environment-setup) -3. [Purchase Flow](#purchase-flow) -4. [Desktop App Integration](#desktop-app-integration) -5. [API Reference](#api-reference) -6. [Testing Scenarios](#testing-scenarios) - ---- - -## Feature Checklist - -### Marketing Site - -| Page | Feature | Status | -|------|---------|--------| -| **Landing** | Hero section with animations | ⬜ | -| **Landing** | Features grid (12 cards) | ⬜ | -| **Landing** | Pricing cards (Free vs Pro) | ⬜ | -| **Landing** | Comparison table | ⬜ | -| **Landing** | FAQ accordion | ⬜ | -| **Landing** | CTA section | ⬜ | -| **Landing** | Footer with links | ⬜ | -| **Landing** | Mobile responsive | ⬜ | -| **Download** | Platform cards (macOS, Windows, Linux) | ⬜ | -| **Download** | Download links work | ⬜ | -| **Download** | System requirements shown | ⬜ | - -### Screenshots to Add - -Replace these placeholder locations with actual screenshots: - -| Location | File | Recommended Size | -|----------|------|------------------| -| Hero section | `public/screenshots/hero.png` | 1920×1080 | -| Query Editor | `public/screenshots/editor.png` | 1200×750 | -| ER Diagrams | `public/screenshots/erd.png` | 1200×750 | - -### Backend APIs - -| Endpoint | Test Command | Expected | -|----------|--------------|----------| -| License Validate | `curl -X POST /api/license/validate` | Returns validation status | -| License Activate | `curl -X POST /api/license/activate` | Creates activation | -| License Deactivate | `curl -X POST /api/license/deactivate` | Removes activation | -| Update Check | `curl /api/updates/check?version=1.0.0` | Returns update info | -| Dodo Webhook | POST with signature | Creates license | - ---- - -## Environment Setup - -### 1. Database (Supabase or Neon) - -```bash -# Create a PostgreSQL database, then: -cd apps/web -cp .env.example .env.local -``` - -Add your database URL: -```env -DATABASE_URL="postgresql://user:password@host:5432/database?sslmode=require" -``` - -Run migrations: -```bash -pnpm db:push -``` - -### 2. Clerk Authentication - -1. Create account at [clerk.com](https://clerk.com) -2. Create a new application -3. Copy keys to `.env.local`: - -```env -NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..." -CLERK_SECRET_KEY="sk_test_..." -``` - -### 3. DodoPayments - -1. Create account at [dodopayments.com](https://dodopayments.com) -2. Create a product: - - Name: `data-peek Pro License` - - Type: One-time payment - - Price: $29 (or $99 regular) -3. Set up webhook: - - URL: `https://your-domain.com/api/webhooks/dodo` - - Events: `payment.completed`, `payment.refunded` -4. Copy credentials: - -```env -DODO_API_KEY="..." -DODO_WEBHOOK_SECRET="..." -``` - -### 4. Resend (Email) - -1. Create account at [resend.com](https://resend.com) -2. Verify your domain -3. Create API key: - -```env -RESEND_API_KEY="re_..." -``` - ---- - -## Purchase Flow - -### How It Works - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PURCHASE FLOW │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ 1. User clicks "Get Pro — $29" on website │ -│ ↓ │ -│ 2. Redirected to DodoPayments checkout │ -│ ↓ │ -│ 3. User completes payment │ -│ ↓ │ -│ 4. DodoPayments sends webhook to /api/webhooks/dodo │ -│ ↓ │ -│ 5. Backend creates customer + license in database │ -│ ↓ │ -│ 6. Welcome email sent with license key │ -│ ↓ │ -│ 7. User enters key in desktop app → activated! │ -│ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### License Key Format - -``` -DPRO-XXXX-XXXX-XXXX-XXXX -``` - -- Prefix: `DPRO` (Pro), `DTEAM` (Team), `DENT` (Enterprise) -- 4 groups of 4 alphanumeric characters -- No confusing characters (0, O, 1, I, l excluded) - -### Setting Up the Buy Button - -Update the pricing component to link to DodoPayments: - -```tsx -// src/components/marketing/pricing.tsx - -// Replace href with your DodoPayments checkout link -{ - cta: 'Get Pro License', - href: 'https://checkout.dodopayments.com/buy/your-product-id', - // Or use their SDK for embedded checkout -} -``` - -### Testing Purchases Locally - -1. Use DodoPayments test mode -2. Use webhook testing tool (ngrok or similar): - -```bash -ngrok http 3000 -# Use the ngrok URL for webhook endpoint -``` - -3. Make a test purchase -4. Check database for new license: - -```bash -pnpm db:studio -# Opens Drizzle Studio to inspect data -``` - ---- - -## Desktop App Integration - -### 1. Install Dependencies - -```bash -cd apps/desktop -pnpm add node-machine-id -``` - -### 2. Add License Types - -Create `src/shared/license.ts`: - -```typescript -export interface LicenseInfo { - valid: boolean - plan: 'free' | 'pro' | 'team' | 'enterprise' - status: 'active' | 'revoked' | 'expired' - updatesUntil: string - activationsUsed: number - activationsMax: number -} - -export interface ActivationInfo { - id: string - deviceId: string - deviceName: string | null - activatedAt: string -} - -export interface ActivateResponse { - success: boolean - activation?: ActivationInfo - license?: LicenseInfo - error?: string -} -``` - -### 3. Create License Service (Main Process) - -Create `src/main/license.ts`: - -```typescript -import { machineIdSync } from 'node-machine-id' -import { app } from 'electron' -import Store from 'electron-store' -import os from 'os' - -const store = new Store() -const API_BASE = 'https://datapeek.app/api' // or your domain - -// Get unique device identifier -export function getDeviceId(): string { - return machineIdSync() -} - -export function getDeviceInfo() { - return { - deviceId: getDeviceId(), - deviceName: os.hostname(), - os: process.platform, // darwin, win32, linux - appVersion: app.getVersion(), - } -} - -// Validate license with server -export async function validateLicense(licenseKey: string): Promise { - const response = await fetch(`${API_BASE}/license/validate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - licenseKey, - deviceId: getDeviceId(), - }), - }) - - return response.json() -} - -// Activate license on this device -export async function activateLicense(licenseKey: string): Promise { - const deviceInfo = getDeviceInfo() - - const response = await fetch(`${API_BASE}/license/activate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - licenseKey, - ...deviceInfo, - }), - }) - - const result = await response.json() - - if (result.success) { - // Store license locally - store.set('license', { - key: licenseKey, - ...result.license, - lastValidated: new Date().toISOString(), - }) - } - - return result -} - -// Deactivate this device -export async function deactivateLicense(licenseKey: string): Promise<{ success: boolean }> { - const response = await fetch(`${API_BASE}/license/deactivate`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - licenseKey, - deviceId: getDeviceId(), - }), - }) - - if (response.ok) { - store.delete('license') - } - - return response.json() -} - -// Get stored license -export function getStoredLicense() { - return store.get('license') as StoredLicense | undefined -} - -// Check license on app startup -export async function checkLicenseOnStartup(): Promise { - const stored = getStoredLicense() - - if (!stored) { - return { status: 'free', features: FREE_FEATURES } - } - - try { - // Try online validation - const result = await validateLicense(stored.key) - - if (result.valid) { - // Update cache - store.set('license', { - ...stored, - ...result, - lastValidated: new Date().toISOString(), - }) - return { status: 'pro', features: PRO_FEATURES, license: result } - } else { - // License revoked or invalid - store.delete('license') - return { status: 'free', features: FREE_FEATURES, error: 'License invalid' } - } - } catch (error) { - // Offline - use cached validation with grace period - const lastValidated = new Date(stored.lastValidated) - const gracePeriod = 14 * 24 * 60 * 60 * 1000 // 14 days - - if (Date.now() - lastValidated.getTime() < gracePeriod) { - return { status: 'pro', features: PRO_FEATURES, offline: true } - } - - return { status: 'free', features: FREE_FEATURES, error: 'License validation failed' } - } -} -``` - -### 4. Add IPC Handlers - -In `src/main/index.ts`: - -```typescript -import { - checkLicenseOnStartup, - activateLicense, - deactivateLicense, - getStoredLicense, -} from './license' - -// License IPC handlers -ipcMain.handle('license:check', async () => { - return checkLicenseOnStartup() -}) - -ipcMain.handle('license:activate', async (_, licenseKey: string) => { - return activateLicense(licenseKey) -}) - -ipcMain.handle('license:deactivate', async (_, licenseKey: string) => { - return deactivateLicense(licenseKey) -}) - -ipcMain.handle('license:get', async () => { - return getStoredLicense() -}) -``` - -### 5. Update Preload Script - -In `src/preload/index.ts`: - -```typescript -// Add to the API object -license: { - check: () => ipcRenderer.invoke('license:check'), - activate: (key: string) => ipcRenderer.invoke('license:activate', key), - deactivate: (key: string) => ipcRenderer.invoke('license:deactivate', key), - get: () => ipcRenderer.invoke('license:get'), -} -``` - -### 6. Create License Store (Renderer) - -Create `src/renderer/src/stores/license-store.ts`: - -```typescript -import { create } from 'zustand' - -interface LicenseState { - status: 'loading' | 'free' | 'pro' | 'team' | 'enterprise' - license: LicenseInfo | null - isOffline: boolean - error: string | null - - // Actions - checkLicense: () => Promise - activateLicense: (key: string) => Promise<{ success: boolean; error?: string }> - deactivateLicense: () => Promise - - // Feature checks - isPro: () => boolean - canUseFeature: (feature: string) => boolean -} - -// Feature limits for free tier -const FREE_LIMITS = { - connections: 2, - queryHistory: 50, - tabs: 3, - erDiagrams: 1, -} - -export const useLicenseStore = create((set, get) => ({ - status: 'loading', - license: null, - isOffline: false, - error: null, - - checkLicense: async () => { - try { - const result = await window.api.license.check() - set({ - status: result.status, - license: result.license ?? null, - isOffline: result.offline ?? false, - error: result.error ?? null, - }) - } catch (error) { - set({ status: 'free', error: 'Failed to check license' }) - } - }, - - activateLicense: async (key: string) => { - try { - const result = await window.api.license.activate(key) - if (result.success) { - set({ - status: result.license?.plan ?? 'pro', - license: result.license ?? null, - error: null, - }) - return { success: true } - } - return { success: false, error: result.error } - } catch (error) { - return { success: false, error: 'Activation failed' } - } - }, - - deactivateLicense: async () => { - const license = get().license - if (license) { - await window.api.license.deactivate(license.key) - } - set({ status: 'free', license: null }) - }, - - isPro: () => { - const status = get().status - return status === 'pro' || status === 'team' || status === 'enterprise' - }, - - canUseFeature: (feature: string) => { - const isPro = get().isPro() - if (isPro) return true - - // Check free tier limits - switch (feature) { - case 'unlimited-connections': - case 'unlimited-history': - case 'unlimited-tabs': - case 'unlimited-erd': - case 'inline-editing': - case 'query-plans': - return false - default: - return true - } - }, -})) -``` - -### 7. Create License Dialog Component - -Create `src/renderer/src/components/license-dialog.tsx`: - -```tsx -import { useState } from 'react' -import { useLicenseStore } from '@/stores/license-store' -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Key, Check, AlertCircle, ExternalLink } from 'lucide-react' - -interface LicenseDialogProps { - open: boolean - onOpenChange: (open: boolean) => void -} - -export function LicenseDialog({ open, onOpenChange }: LicenseDialogProps) { - const [licenseKey, setLicenseKey] = useState('') - const [isLoading, setIsLoading] = useState(false) - const [error, setError] = useState(null) - - const { status, license, activateLicense, deactivateLicense } = useLicenseStore() - const isPro = status !== 'free' && status !== 'loading' - - const handleActivate = async () => { - if (!licenseKey.trim()) return - - setIsLoading(true) - setError(null) - - const result = await activateLicense(licenseKey.trim()) - - setIsLoading(false) - - if (result.success) { - setLicenseKey('') - onOpenChange(false) - } else { - setError(result.error ?? 'Activation failed') - } - } - - const handleDeactivate = async () => { - setIsLoading(true) - await deactivateLicense() - setIsLoading(false) - } - - return ( - - - - - - License - - - - {isPro ? ( - // Pro license view -
-
-
- - Pro License Active -
-

- Updates until: {new Date(license?.updatesUntil ?? '').toLocaleDateString()} -

-

- Activations: {license?.activationsUsed} / {license?.activationsMax} -

-
- - -
- ) : ( - // Free tier view -
-
- - setLicenseKey(e.target.value.toUpperCase())} - className="font-mono" - /> -
- - {error && ( -
- - {error} -
- )} - - - - -
- )} -
-
- ) -} -``` - -### 8. Feature Gating Example - -```tsx -// Example: Gating the "Add Connection" button - -import { useLicenseStore } from '@/stores/license-store' - -function ConnectionList() { - const { isPro, canUseFeature } = useLicenseStore() - const connections = useConnectionStore((s) => s.connections) - - const canAddConnection = isPro() || connections.length < 2 - - return ( -
- {/* ... connection list ... */} - - - - {!canAddConnection && ( -

- Free tier limited to 2 connections. - Upgrade to Pro -

- )} -
- ) -} -``` - ---- - -## API Reference - -### POST /api/license/validate - -Validate a license key and check if device is activated. - -**Request:** -```json -{ - "licenseKey": "DPRO-XXXX-XXXX-XXXX-XXXX", - "deviceId": "unique-machine-id" -} -``` - -**Response:** -```json -{ - "valid": true, - "plan": "pro", - "status": "active", - "updatesUntil": "2025-11-28T00:00:00.000Z", - "activationsUsed": 1, - "activationsMax": 3 -} -``` - -### POST /api/license/activate - -Activate a license on a new device. - -**Request:** -```json -{ - "licenseKey": "DPRO-XXXX-XXXX-XXXX-XXXX", - "deviceId": "unique-machine-id", - "deviceName": "MacBook Pro", - "os": "darwin", - "appVersion": "1.0.0" -} -``` - -**Response:** -```json -{ - "success": true, - "activation": { - "id": "uuid", - "deviceId": "unique-machine-id", - "deviceName": "MacBook Pro", - "activatedAt": "2024-11-28T00:00:00.000Z" - }, - "license": { - "plan": "pro", - "updatesUntil": "2025-11-28T00:00:00.000Z", - "activationsUsed": 1, - "activationsMax": 3 - } -} -``` - -### POST /api/license/deactivate - -Deactivate a device. - -**Request:** -```json -{ - "licenseKey": "DPRO-XXXX-XXXX-XXXX-XXXX", - "deviceId": "unique-machine-id" -} -``` - -**Response:** -```json -{ - "success": true, - "activationsRemaining": 2 -} -``` - -### GET /api/updates/check - -Check for app updates. - -**Request:** -``` -GET /api/updates/check?version=1.0.0&platform=macos-arm -``` - -**Response:** -```json -{ - "hasUpdate": true, - "latestVersion": "1.1.0", - "currentVersion": "1.0.0", - "downloadUrl": "https://...", - "releaseNotes": "Bug fixes and improvements", - "forceUpdate": false -} -``` - ---- - -## Testing Scenarios - -### Manual Test Checklist - -| Scenario | Steps | Expected Result | -|----------|-------|-----------------| -| **Fresh install (free)** | Open app with no license | Free tier limits apply | -| **Valid activation** | Enter valid license key | Unlocks Pro features | -| **Invalid key** | Enter random key | Shows error message | -| **Max activations** | Activate on 4th device | Shows "max reached" error | -| **Deactivate** | Deactivate from settings | Reverts to free tier | -| **Offline mode** | Disconnect internet, open app | Uses cached license (14 day grace) | -| **Revoked license** | Revoke via webhook | Shows "revoked" error on next validation | -| **Update check** | Use older version | Shows update available | - -### Test License Keys - -For development, you can manually insert test licenses: - -```sql --- Insert test customer -INSERT INTO customers (email, name) -VALUES ('test@example.com', 'Test User'); - --- Insert test license (get customer ID from above) -INSERT INTO licenses (customer_id, license_key, plan, status, max_activations, updates_until) -VALUES ( - 'customer-uuid-here', - 'DPRO-TEST-TEST-TEST-TEST', - 'pro', - 'active', - 3, - NOW() + INTERVAL '1 year' -); -``` - ---- - -## Deployment Checklist - -- [ ] Database migrations run on production -- [ ] Environment variables set on Vercel/hosting -- [ ] DodoPayments webhook URL updated to production -- [ ] Clerk production keys configured -- [ ] Resend domain verified -- [ ] Download links point to actual releases -- [ ] Screenshots added to marketing site - ---- - -*Document created: November 2024*