Spaces:
Sleeping
Sleeping
Deploy Mastra SQL agent
Browse files- .dockerignore +10 -0
- .env.example +1 -0
- .gitignore +8 -0
- CONTRIBUTING.md +12 -0
- Dockerfile +22 -0
- README.md +9 -6
- package-lock.json +0 -0
- package.json +33 -0
- src/mastra/agents/sql-agent.ts +44 -0
- src/mastra/index.ts +31 -0
- src/mastra/tools/execute-sql.ts +44 -0
- src/mastra/tools/introspect-database.ts +54 -0
- src/seed.ts +174 -0
- tsconfig.json +14 -0
.dockerignore
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
| 2 |
+
.mastra
|
| 3 |
+
dist
|
| 4 |
+
.env
|
| 5 |
+
.env.*
|
| 6 |
+
*.db
|
| 7 |
+
*.db-*
|
| 8 |
+
.git
|
| 9 |
+
.gitignore
|
| 10 |
+
output.txt
|
.env.example
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
OPENAI_API_KEY=
|
.gitignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
output.txt
|
| 2 |
+
node_modules
|
| 3 |
+
dist
|
| 4 |
+
.mastra
|
| 5 |
+
.env.development
|
| 6 |
+
.env
|
| 7 |
+
*.db
|
| 8 |
+
*.db-*
|
CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing
|
| 2 |
+
|
| 3 |
+
This repository is auto-generated from the [Mastra monorepo](https://github.com/mastra-ai/mastra). Pull requests opened here will be ignored.
|
| 4 |
+
|
| 5 |
+
To contribute:
|
| 6 |
+
|
| 7 |
+
1. Fork the [Mastra monorepo](https://github.com/mastra-ai/mastra)
|
| 8 |
+
2. Find this template in `templates/template-text-to-sql`
|
| 9 |
+
3. Make your changes
|
| 10 |
+
4. Open a pull request against the monorepo
|
| 11 |
+
|
| 12 |
+
A bot syncs accepted changes to this repository.
|
Dockerfile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:22-bookworm-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY package.json package-lock.json ./
|
| 6 |
+
|
| 7 |
+
RUN npm ci
|
| 8 |
+
|
| 9 |
+
COPY . .
|
| 10 |
+
|
| 11 |
+
RUN npm run seed
|
| 12 |
+
RUN npm run build -- --studio
|
| 13 |
+
|
| 14 |
+
ENV PORT=7860
|
| 15 |
+
ENV MASTRA_HOST=0.0.0.0
|
| 16 |
+
ENV MASTRA_STUDIO_PATH=studio
|
| 17 |
+
|
| 18 |
+
EXPOSE 7860
|
| 19 |
+
|
| 20 |
+
WORKDIR /app/.mastra/output
|
| 21 |
+
|
| 22 |
+
CMD ["node", "index.mjs"]
|
README.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
| 1 |
---
|
| 2 |
-
title: My
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
|
|
|
| 7 |
pinned: false
|
| 8 |
-
license: apache-2.0
|
| 9 |
-
short_description: Mastra AI agent converting conversational English into SQL.
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: My SQL Agent
|
| 3 |
+
emoji: 🗄️
|
| 4 |
+
colorFrom: blue
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
pinned: false
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# My SQL Agent
|
| 12 |
+
|
| 13 |
+
A Mastra Text-to-SQL agent deployed with Docker on Hugging Face Spaces.
|
| 14 |
+
|
| 15 |
+
The agent converts natural-language questions into safe SQLite `SELECT` queries and returns results from the sample company database.
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "template-text-to-sql",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "A Mastra template for natural language to SQL conversion using a local SQLite database.",
|
| 5 |
+
"main": "index.js",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"seed": "tsx src/seed.ts",
|
| 8 |
+
"dev": "tsx src/seed.ts && mastra dev",
|
| 9 |
+
"build": "mastra build",
|
| 10 |
+
"start": "mastra start"
|
| 11 |
+
},
|
| 12 |
+
"license": "Apache-2.0",
|
| 13 |
+
"packageManager": "pnpm@11.3.0",
|
| 14 |
+
"type": "module",
|
| 15 |
+
"engines": {
|
| 16 |
+
"node": ">=22.13.0"
|
| 17 |
+
},
|
| 18 |
+
"dependencies": {
|
| 19 |
+
"@libsql/client": "^0.17.0",
|
| 20 |
+
"@mastra/core": "latest",
|
| 21 |
+
"@mastra/libsql": "latest",
|
| 22 |
+
"@mastra/loggers": "latest",
|
| 23 |
+
"@mastra/memory": "latest",
|
| 24 |
+
"@mastra/observability": "latest",
|
| 25 |
+
"zod": "^4.3.6"
|
| 26 |
+
},
|
| 27 |
+
"devDependencies": {
|
| 28 |
+
"@types/node": "^22.19.7",
|
| 29 |
+
"mastra": "latest",
|
| 30 |
+
"tsx": "^4.19.4",
|
| 31 |
+
"typescript": "^5.9.3"
|
| 32 |
+
}
|
| 33 |
+
}
|
src/mastra/agents/sql-agent.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Agent } from '@mastra/core/agent';
|
| 2 |
+
import { Memory } from '@mastra/memory';
|
| 3 |
+
import { introspectDatabase } from '../tools/introspect-database';
|
| 4 |
+
import { executeSql } from '../tools/execute-sql';
|
| 5 |
+
|
| 6 |
+
export const sqlAgent = new Agent({
|
| 7 |
+
id: 'sql-agent',
|
| 8 |
+
name: 'SQL Agent',
|
| 9 |
+
model: 'opencode-go/deepseek-v4-flash',
|
| 10 |
+
instructions: `You are a SQL assistant that helps users query a local SQLite database using natural language.
|
| 11 |
+
|
| 12 |
+
## Tools
|
| 13 |
+
|
| 14 |
+
You have two tools:
|
| 15 |
+
- **introspect-database**: Returns the database schema (tables, columns, types, foreign keys, row counts). Always call this first before writing any SQL so you know what's available.
|
| 16 |
+
- **execute-sql**: Runs a SELECT query and returns the results. Only SELECT queries are allowed.
|
| 17 |
+
|
| 18 |
+
## Workflow
|
| 19 |
+
|
| 20 |
+
1. When the user asks a question, first call introspect-database to understand the schema.
|
| 21 |
+
2. Convert the user's natural language question into a SQLite-compatible SELECT query.
|
| 22 |
+
3. Call execute-sql with the generated query.
|
| 23 |
+
4. Present the results in a clear, readable format (use tables when appropriate).
|
| 24 |
+
|
| 25 |
+
## SQL Guidelines
|
| 26 |
+
|
| 27 |
+
- Generate only SELECT queries. Never generate INSERT, UPDATE, DELETE, DROP, or any other mutating statements.
|
| 28 |
+
- Use SQLite syntax: LIKE instead of ILIKE, no SERIAL, use INTEGER PRIMARY KEY.
|
| 29 |
+
- Use proper JOINs when the question involves data across multiple tables.
|
| 30 |
+
- Use aggregate functions (COUNT, SUM, AVG, MIN, MAX) when the user asks for summaries.
|
| 31 |
+
- Use GROUP BY with aggregate functions.
|
| 32 |
+
- Use ORDER BY and LIMIT for "top N" style questions.
|
| 33 |
+
- Alias columns for readability (e.g., COUNT(*) AS total_employees).
|
| 34 |
+
- When the user's question is ambiguous, explain your interpretation before executing.
|
| 35 |
+
|
| 36 |
+
## Response Format
|
| 37 |
+
|
| 38 |
+
- Show the SQL query you generated so the user can learn from it.
|
| 39 |
+
- Present results clearly. For tabular data, format as a markdown table.
|
| 40 |
+
- If the query returns no results, explain possible reasons.
|
| 41 |
+
- If you're unsure about the schema, call introspect-database again.`,
|
| 42 |
+
tools: { introspectDatabase, executeSql },
|
| 43 |
+
memory: new Memory(),
|
| 44 |
+
});
|
src/mastra/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Mastra } from '@mastra/core/mastra';
|
| 2 |
+
import { PinoLogger } from '@mastra/loggers';
|
| 3 |
+
import { LibSQLStore } from '@mastra/libsql';
|
| 4 |
+
import {
|
| 5 |
+
Observability,
|
| 6 |
+
MastraStorageExporter,
|
| 7 |
+
MastraPlatformExporter,
|
| 8 |
+
SensitiveDataFilter,
|
| 9 |
+
} from '@mastra/observability';
|
| 10 |
+
import { sqlAgent } from './agents/sql-agent';
|
| 11 |
+
|
| 12 |
+
export const mastra = new Mastra({
|
| 13 |
+
agents: { sqlAgent },
|
| 14 |
+
storage: new LibSQLStore({
|
| 15 |
+
id: 'mastra-storage',
|
| 16 |
+
url: 'file:./mastra.db',
|
| 17 |
+
}),
|
| 18 |
+
logger: new PinoLogger({
|
| 19 |
+
name: 'Mastra Text-to-SQL',
|
| 20 |
+
level: 'info',
|
| 21 |
+
}),
|
| 22 |
+
observability: new Observability({
|
| 23 |
+
configs: {
|
| 24 |
+
default: {
|
| 25 |
+
serviceName: 'text-to-sql',
|
| 26 |
+
exporters: [new MastraStorageExporter(), new MastraPlatformExporter()],
|
| 27 |
+
spanOutputProcessors: [new SensitiveDataFilter()],
|
| 28 |
+
},
|
| 29 |
+
},
|
| 30 |
+
}),
|
| 31 |
+
});
|
src/mastra/tools/execute-sql.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createTool } from '@mastra/core/tools';
|
| 2 |
+
import { createClient } from '@libsql/client';
|
| 3 |
+
import { z } from 'zod';
|
| 4 |
+
|
| 5 |
+
const db = createClient({ url: 'file:./data.db' });
|
| 6 |
+
|
| 7 |
+
const BLOCKED_PATTERNS = [
|
| 8 |
+
/\b(INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|TRUNCATE|REPLACE)\b/i,
|
| 9 |
+
/\b(ATTACH|DETACH)\b/i,
|
| 10 |
+
/\b(PRAGMA)\b/i,
|
| 11 |
+
/;.*\S/, // multiple statements
|
| 12 |
+
];
|
| 13 |
+
|
| 14 |
+
export const executeSql = createTool({
|
| 15 |
+
id: 'execute-sql',
|
| 16 |
+
description: 'Executes a read-only SQL SELECT query against the local SQLite database and returns the results.',
|
| 17 |
+
inputSchema: z.object({
|
| 18 |
+
query: z.string().describe('The SQL SELECT query to execute'),
|
| 19 |
+
}),
|
| 20 |
+
outputSchema: z.object({
|
| 21 |
+
rows: z.array(z.record(z.string(), z.unknown())).describe('Query result rows'),
|
| 22 |
+
rowCount: z.number().describe('Number of rows returned'),
|
| 23 |
+
}),
|
| 24 |
+
execute: async ({ query }) => {
|
| 25 |
+
const trimmed = query.trim().replace(/;$/, '');
|
| 26 |
+
|
| 27 |
+
for (const pattern of BLOCKED_PATTERNS) {
|
| 28 |
+
if (pattern.test(trimmed)) {
|
| 29 |
+
throw new Error('Only SELECT queries are allowed.');
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
if (!/^\s*SELECT\b/i.test(trimmed)) {
|
| 34 |
+
throw new Error('Query must start with SELECT.');
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
const result = await db.execute(trimmed);
|
| 38 |
+
|
| 39 |
+
return {
|
| 40 |
+
rows: result.rows as Record<string, unknown>[],
|
| 41 |
+
rowCount: result.rows.length,
|
| 42 |
+
};
|
| 43 |
+
},
|
| 44 |
+
});
|
src/mastra/tools/introspect-database.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createTool } from '@mastra/core/tools';
|
| 2 |
+
import { createClient } from '@libsql/client';
|
| 3 |
+
import { z } from 'zod';
|
| 4 |
+
|
| 5 |
+
const db = createClient({ url: 'file:./data.db' });
|
| 6 |
+
|
| 7 |
+
export const introspectDatabase = createTool({
|
| 8 |
+
id: 'introspect-database',
|
| 9 |
+
description:
|
| 10 |
+
'Introspects the local SQLite database and returns a description of all tables, columns, foreign keys, and row counts.',
|
| 11 |
+
inputSchema: z.object({}),
|
| 12 |
+
outputSchema: z.object({
|
| 13 |
+
schema: z.string().describe('Human-readable database schema description'),
|
| 14 |
+
}),
|
| 15 |
+
execute: async () => {
|
| 16 |
+
const tables = await db.execute(
|
| 17 |
+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_litestream_%' ORDER BY name",
|
| 18 |
+
);
|
| 19 |
+
|
| 20 |
+
const lines: string[] = ['# Database Schema', ''];
|
| 21 |
+
|
| 22 |
+
for (const table of tables.rows) {
|
| 23 |
+
const tableName = table.name as string;
|
| 24 |
+
|
| 25 |
+
const columns = await db.execute(`PRAGMA table_info('${tableName}')`);
|
| 26 |
+
const foreignKeys = await db.execute(`PRAGMA foreign_key_list('${tableName}')`);
|
| 27 |
+
const countResult = await db.execute(`SELECT COUNT(*) as count FROM "${tableName}"`);
|
| 28 |
+
const rowCount = countResult.rows[0].count;
|
| 29 |
+
|
| 30 |
+
lines.push(`## ${tableName} (${rowCount} rows)`);
|
| 31 |
+
lines.push('');
|
| 32 |
+
lines.push('| Column | Type | Nullable | PK |');
|
| 33 |
+
lines.push('|--------|------|----------|----|');
|
| 34 |
+
|
| 35 |
+
for (const col of columns.rows) {
|
| 36 |
+
const nullable = col.notnull ? 'NO' : 'YES';
|
| 37 |
+
const pk = col.pk ? 'YES' : '';
|
| 38 |
+
lines.push(`| ${col.name} | ${col.type || 'ANY'} | ${nullable} | ${pk} |`);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
if (foreignKeys.rows.length > 0) {
|
| 42 |
+
lines.push('');
|
| 43 |
+
lines.push('**Foreign Keys:**');
|
| 44 |
+
for (const fk of foreignKeys.rows) {
|
| 45 |
+
lines.push(`- ${fk.from} → ${fk.table}.${fk.to}`);
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
lines.push('');
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
return { schema: lines.join('\n') };
|
| 53 |
+
},
|
| 54 |
+
});
|
src/seed.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { createClient } from '@libsql/client';
|
| 2 |
+
import { mkdirSync } from 'node:fs';
|
| 3 |
+
|
| 4 |
+
const PUBLIC_DIR = 'src/mastra/public';
|
| 5 |
+
const DB_PATH = `file:${PUBLIC_DIR}/data.db`;
|
| 6 |
+
|
| 7 |
+
async function seed() {
|
| 8 |
+
mkdirSync(PUBLIC_DIR, { recursive: true });
|
| 9 |
+
const client = createClient({ url: DB_PATH });
|
| 10 |
+
|
| 11 |
+
// Check if already seeded
|
| 12 |
+
const existing = await client.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='companies'");
|
| 13 |
+
if (existing.rows.length > 0) {
|
| 14 |
+
console.log('Database already seeded, skipping.');
|
| 15 |
+
return;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
console.log('Seeding database...');
|
| 19 |
+
|
| 20 |
+
await client.executeMultiple(`
|
| 21 |
+
CREATE TABLE companies (
|
| 22 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 23 |
+
name TEXT NOT NULL,
|
| 24 |
+
industry TEXT,
|
| 25 |
+
founded INTEGER,
|
| 26 |
+
employee_count INTEGER,
|
| 27 |
+
revenue INTEGER,
|
| 28 |
+
headquarters TEXT
|
| 29 |
+
);
|
| 30 |
+
|
| 31 |
+
CREATE TABLE departments (
|
| 32 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 33 |
+
company_id INTEGER REFERENCES companies(id),
|
| 34 |
+
name TEXT NOT NULL,
|
| 35 |
+
budget INTEGER,
|
| 36 |
+
head_count INTEGER
|
| 37 |
+
);
|
| 38 |
+
|
| 39 |
+
CREATE TABLE employees (
|
| 40 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 41 |
+
company_id INTEGER REFERENCES companies(id),
|
| 42 |
+
department_id INTEGER REFERENCES departments(id),
|
| 43 |
+
first_name TEXT NOT NULL,
|
| 44 |
+
last_name TEXT NOT NULL,
|
| 45 |
+
email TEXT UNIQUE,
|
| 46 |
+
hire_date TEXT,
|
| 47 |
+
salary INTEGER,
|
| 48 |
+
title TEXT,
|
| 49 |
+
status TEXT DEFAULT 'Active'
|
| 50 |
+
);
|
| 51 |
+
|
| 52 |
+
CREATE TABLE projects (
|
| 53 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 54 |
+
company_id INTEGER REFERENCES companies(id),
|
| 55 |
+
department_id INTEGER REFERENCES departments(id),
|
| 56 |
+
name TEXT NOT NULL,
|
| 57 |
+
status TEXT,
|
| 58 |
+
budget INTEGER,
|
| 59 |
+
start_date TEXT,
|
| 60 |
+
end_date TEXT
|
| 61 |
+
);
|
| 62 |
+
|
| 63 |
+
CREATE TABLE project_assignments (
|
| 64 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 65 |
+
project_id INTEGER REFERENCES projects(id),
|
| 66 |
+
employee_id INTEGER REFERENCES employees(id),
|
| 67 |
+
role TEXT,
|
| 68 |
+
UNIQUE(project_id, employee_id)
|
| 69 |
+
);
|
| 70 |
+
|
| 71 |
+
-- Companies
|
| 72 |
+
INSERT INTO companies (name, industry, founded, employee_count, revenue, headquarters) VALUES
|
| 73 |
+
('Acme Corp', 'Technology', 2010, 150, 25000000, 'San Francisco, CA'),
|
| 74 |
+
('Globex Inc', 'Finance', 2005, 300, 80000000, 'New York, NY'),
|
| 75 |
+
('Initech', 'Healthcare', 2015, 80, 12000000, 'Austin, TX'),
|
| 76 |
+
('Umbrella Ltd', 'Retail', 2008, 200, 45000000, 'Chicago, IL'),
|
| 77 |
+
('Stark Industries', 'Manufacturing', 2000, 500, 120000000, 'Detroit, MI');
|
| 78 |
+
|
| 79 |
+
-- Departments
|
| 80 |
+
INSERT INTO departments (company_id, name, budget, head_count) VALUES
|
| 81 |
+
(1, 'Engineering', 5000000, 60),
|
| 82 |
+
(1, 'Marketing', 2000000, 25),
|
| 83 |
+
(1, 'Sales', 3000000, 35),
|
| 84 |
+
(1, 'Human Resources', 1000000, 15),
|
| 85 |
+
(2, 'Investment Banking', 10000000, 80),
|
| 86 |
+
(2, 'Risk Management', 4000000, 40),
|
| 87 |
+
(2, 'Compliance', 2000000, 30),
|
| 88 |
+
(2, 'Technology', 6000000, 50),
|
| 89 |
+
(3, 'Research', 4000000, 30),
|
| 90 |
+
(3, 'Clinical', 3000000, 25),
|
| 91 |
+
(3, 'Operations', 2000000, 15),
|
| 92 |
+
(4, 'Supply Chain', 5000000, 50),
|
| 93 |
+
(4, 'Marketing', 3000000, 40),
|
| 94 |
+
(4, 'Customer Service', 2000000, 35),
|
| 95 |
+
(5, 'Manufacturing', 8000000, 150),
|
| 96 |
+
(5, 'Engineering', 6000000, 100),
|
| 97 |
+
(5, 'Quality Assurance', 3000000, 50);
|
| 98 |
+
|
| 99 |
+
-- Employees
|
| 100 |
+
INSERT INTO employees (company_id, department_id, first_name, last_name, email, hire_date, salary, title, status) VALUES
|
| 101 |
+
(1, 1, 'Alice', 'Chen', 'alice.chen@acme.com', '2018-03-15', 145000, 'Senior Engineer', 'Active'),
|
| 102 |
+
(1, 1, 'Bob', 'Martinez', 'bob.martinez@acme.com', '2020-07-01', 125000, 'Software Engineer', 'Active'),
|
| 103 |
+
(1, 1, 'Carol', 'Johnson', 'carol.johnson@acme.com', '2019-01-10', 155000, 'Staff Engineer', 'Active'),
|
| 104 |
+
(1, 1, 'David', 'Kim', 'david.kim@acme.com', '2021-06-20', 110000, 'Junior Engineer', 'Active'),
|
| 105 |
+
(1, 2, 'Eve', 'Williams', 'eve.williams@acme.com', '2019-11-05', 95000, 'Marketing Manager', 'Active'),
|
| 106 |
+
(1, 2, 'Frank', 'Brown', 'frank.brown@acme.com', '2022-02-14', 75000, 'Marketing Specialist', 'Active'),
|
| 107 |
+
(1, 3, 'Grace', 'Davis', 'grace.davis@acme.com', '2020-09-01', 130000, 'Sales Director', 'Active'),
|
| 108 |
+
(1, 3, 'Henry', 'Wilson', 'henry.wilson@acme.com', '2021-03-22', 85000, 'Sales Representative', 'Active'),
|
| 109 |
+
(1, 4, 'Ivy', 'Taylor', 'ivy.taylor@acme.com', '2017-05-30', 90000, 'HR Manager', 'Active'),
|
| 110 |
+
(2, 5, 'Jack', 'Anderson', 'jack.anderson@globex.com', '2016-08-12', 180000, 'Senior Banker', 'Active'),
|
| 111 |
+
(2, 5, 'Karen', 'Thomas', 'karen.thomas@globex.com', '2018-04-03', 160000, 'Investment Analyst', 'Active'),
|
| 112 |
+
(2, 5, 'Leo', 'Garcia', 'leo.garcia@globex.com', '2020-01-15', 140000, 'Associate Banker', 'Active'),
|
| 113 |
+
(2, 6, 'Maria', 'Rodriguez', 'maria.rodriguez@globex.com', '2017-11-20', 150000, 'Risk Analyst', 'Active'),
|
| 114 |
+
(2, 6, 'Nick', 'Lee', 'nick.lee@globex.com', '2019-06-08', 135000, 'Risk Manager', 'Active'),
|
| 115 |
+
(2, 7, 'Olivia', 'White', 'olivia.white@globex.com', '2021-09-01', 120000, 'Compliance Officer', 'Active'),
|
| 116 |
+
(2, 8, 'Paul', 'Harris', 'paul.harris@globex.com', '2018-02-28', 155000, 'Tech Lead', 'Active'),
|
| 117 |
+
(3, 9, 'Quinn', 'Clark', 'quinn.clark@initech.com', '2019-07-15', 130000, 'Research Scientist', 'Active'),
|
| 118 |
+
(3, 9, 'Rachel', 'Lewis', 'rachel.lewis@initech.com', '2020-10-01', 120000, 'Lab Director', 'Active'),
|
| 119 |
+
(3, 10, 'Sam', 'Walker', 'sam.walker@initech.com', '2021-01-20', 110000, 'Clinical Researcher', 'Active'),
|
| 120 |
+
(3, 11, 'Tina', 'Hall', 'tina.hall@initech.com', '2022-04-10', 85000, 'Operations Manager', 'Active'),
|
| 121 |
+
(4, 12, 'Uma', 'Allen', 'uma.allen@umbrella.com', '2018-06-01', 115000, 'Supply Chain Manager', 'Active'),
|
| 122 |
+
(4, 12, 'Victor', 'Young', 'victor.young@umbrella.com', '2020-03-15', 90000, 'Logistics Coordinator', 'Active'),
|
| 123 |
+
(4, 13, 'Wendy', 'King', 'wendy.king@umbrella.com', '2019-08-20', 100000, 'Brand Manager', 'Active'),
|
| 124 |
+
(4, 14, 'Xavier', 'Scott', 'xavier.scott@umbrella.com', '2021-11-01', 65000, 'Support Specialist', 'Active'),
|
| 125 |
+
(5, 15, 'Yara', 'Green', 'yara.green@stark.com', '2015-04-10', 95000, 'Production Manager', 'Active'),
|
| 126 |
+
(5, 15, 'Zach', 'Adams', 'zach.adams@stark.com', '2017-09-22', 80000, 'Line Supervisor', 'Active'),
|
| 127 |
+
(5, 16, 'Amy', 'Nelson', 'amy.nelson@stark.com', '2016-12-05', 140000, 'Principal Engineer', 'Active'),
|
| 128 |
+
(5, 16, 'Brian', 'Carter', 'brian.carter@stark.com', '2019-02-18', 120000, 'Mechanical Engineer', 'Active'),
|
| 129 |
+
(5, 17, 'Cindy', 'Mitchell', 'cindy.mitchell@stark.com', '2020-07-30', 105000, 'QA Lead', 'Active'),
|
| 130 |
+
(5, 17, 'Derek', 'Roberts', 'derek.roberts@stark.com', '2022-01-10', 85000, 'QA Analyst', 'Active');
|
| 131 |
+
|
| 132 |
+
-- Projects
|
| 133 |
+
INSERT INTO projects (company_id, department_id, name, status, budget, start_date, end_date) VALUES
|
| 134 |
+
(1, 1, 'Cloud Migration', 'In Progress', 500000, '2024-01-15', '2024-12-31'),
|
| 135 |
+
(1, 1, 'Mobile App v2', 'Planning', 300000, '2024-06-01', '2025-03-31'),
|
| 136 |
+
(1, 2, 'Brand Refresh', 'Completed', 150000, '2023-09-01', '2024-02-28'),
|
| 137 |
+
(2, 5, 'Q4 Fund Launch', 'In Progress', 2000000, '2024-03-01', '2024-09-30'),
|
| 138 |
+
(2, 8, 'Trading Platform Upgrade', 'In Progress', 1500000, '2024-02-01', '2024-11-30'),
|
| 139 |
+
(3, 9, 'Drug Trial Phase 2', 'In Progress', 3000000, '2023-06-01', '2025-06-30'),
|
| 140 |
+
(4, 12, 'Warehouse Automation', 'Planning', 800000, '2024-07-01', '2025-04-30'),
|
| 141 |
+
(5, 16, 'EV Battery Design', 'In Progress', 5000000, '2024-01-01', '2025-12-31'),
|
| 142 |
+
(5, 15, 'Assembly Line Retrofit', 'Completed', 2000000, '2023-03-01', '2024-01-31');
|
| 143 |
+
|
| 144 |
+
-- Project Assignments
|
| 145 |
+
INSERT INTO project_assignments (project_id, employee_id, role) VALUES
|
| 146 |
+
(1, 1, 'Tech Lead'),
|
| 147 |
+
(1, 2, 'Developer'),
|
| 148 |
+
(1, 3, 'Architect'),
|
| 149 |
+
(2, 2, 'Developer'),
|
| 150 |
+
(2, 4, 'Developer'),
|
| 151 |
+
(3, 5, 'Project Manager'),
|
| 152 |
+
(3, 6, 'Designer'),
|
| 153 |
+
(4, 10, 'Lead Banker'),
|
| 154 |
+
(4, 11, 'Analyst'),
|
| 155 |
+
(5, 16, 'Tech Lead'),
|
| 156 |
+
(6, 17, 'Lead Researcher'),
|
| 157 |
+
(6, 18, 'Lab Director'),
|
| 158 |
+
(6, 19, 'Researcher'),
|
| 159 |
+
(7, 21, 'Project Manager'),
|
| 160 |
+
(7, 22, 'Coordinator'),
|
| 161 |
+
(8, 27, 'Lead Engineer'),
|
| 162 |
+
(8, 28, 'Engineer'),
|
| 163 |
+
(9, 25, 'Production Lead'),
|
| 164 |
+
(9, 26, 'Supervisor');
|
| 165 |
+
`);
|
| 166 |
+
|
| 167 |
+
console.log('Database seeded with sample company data.');
|
| 168 |
+
console.log('Tables: companies, departments, employees, projects, project_assignments');
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
seed().catch(err => {
|
| 172 |
+
console.error('Failed to seed database:', err);
|
| 173 |
+
process.exit(1);
|
| 174 |
+
});
|
tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2022",
|
| 4 |
+
"module": "ES2022",
|
| 5 |
+
"moduleResolution": "bundler",
|
| 6 |
+
"esModuleInterop": true,
|
| 7 |
+
"forceConsistentCasingInFileNames": true,
|
| 8 |
+
"strict": true,
|
| 9 |
+
"skipLibCheck": true,
|
| 10 |
+
"noEmit": true,
|
| 11 |
+
"outDir": "dist"
|
| 12 |
+
},
|
| 13 |
+
"include": ["src/**/*"]
|
| 14 |
+
}
|