Skip to content

Commit 55abe41

Browse files
authored
Merge pull request #3 from objectql/copilot/add-web-console-for-database-management
Add web-based admin console for database management
2 parents edfa677 + 29c22c7 commit 55abe41

25 files changed

Lines changed: 1879 additions & 20 deletions

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ It is **not** an ORM, but a high-level data protocol designed for AI agents, low
4848
* Built-in HTTP adapter (`@objectql/server`) to run anywhere (Node, Express, Next.js).
4949
* Standardized JSON API for CRUD and Actions.
5050

51+
* **🎨 Web Console:**
52+
* Beautiful web-based admin interface for database management.
53+
* Browse objects, view data grids, perform CRUD operations.
54+
* Schema inspector to view object definitions and field metadata.
55+
* Modern, responsive design with intuitive navigation.
56+
5157
## 📦 Installation
5258

5359
```bash
@@ -113,6 +119,44 @@ const resultsSql = await app.datasource('runtime').find(query);
113119

114120
```
115121

122+
## 🎨 Web Console
123+
124+
ObjectQL includes a beautiful web-based admin console for database management.
125+
126+
![ObjectQL Console - Home](https://github.com/user-attachments/assets/c3613831-c73b-413e-bd0a-996841c072fb)
127+
128+
### Features
129+
130+
* **Object Browser**: View all registered objects/tables in your database
131+
* **Data Grid**: Browse and search records with pagination
132+
* **CRUD Operations**: Create, read, update, and delete records through an intuitive UI
133+
* **Schema Inspector**: View object definitions and field metadata
134+
135+
![ObjectQL Console - Data Grid](https://github.com/user-attachments/assets/45812de8-4476-4ff1-90cd-bf41515157a5)
136+
137+
### Usage
138+
139+
```typescript
140+
import express from 'express';
141+
import { ObjectQL } from '@objectql/core';
142+
import { createNodeHandler, createMetadataHandler, createConsoleHandler } from '@objectql/server';
143+
144+
const app = new ObjectQL({ /* ... */ });
145+
const server = express();
146+
147+
// API endpoints
148+
server.all('/api/objectql', createNodeHandler(app));
149+
server.all('/api/metadata*', createMetadataHandler(app));
150+
151+
// Serve console UI
152+
server.get('/console*', createConsoleHandler());
153+
154+
server.listen(3004);
155+
// Visit http://localhost:3004/console
156+
```
157+
158+
![ObjectQL Console - Schema Inspector](https://github.com/user-attachments/assets/ee5b9e77-f8c8-4a01-a933-d20d82a424cd)
159+
116160
## 🏗 Architecture
117161

118162
ObjectQL follows a strict separation of concerns, acting as a compiler between your intent (DSL) and the execution (Driver).

examples/express-server/src/index.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import express from 'express';
22
import { ObjectQL } from '@objectql/core';
33
import { KnexDriver } from '@objectql/driver-knex';
4-
import { createNodeHandler } from '@objectql/server';
4+
import { createNodeHandler, createMetadataHandler, createConsoleHandler } from '@objectql/server';
55
import * as path from 'path';
66

77
async function main() {
@@ -22,24 +22,49 @@ async function main() {
2222
app.loadFromDirectory(path.join(__dirname));
2323
await app.init();
2424

25-
// 3. Create Handler
25+
// 3. Create Handlers
2626
const objectQLHandler = createNodeHandler(app);
27+
const metadataHandler = createMetadataHandler(app);
28+
const consoleHandler = createConsoleHandler();
2729

2830
// 4. Setup Express
2931
const server = express();
3032
const port = 3004;
3133

32-
// Optional: Parse JSON body globally (or strict to other routes)
33-
// server.use(express.json());
34+
// Enable CORS for development
35+
server.use((req, res, next) => {
36+
res.header('Access-Control-Allow-Origin', '*');
37+
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
38+
res.header('Access-Control-Allow-Headers', 'Content-Type');
39+
if (req.method === 'OPTIONS') {
40+
res.sendStatus(200);
41+
} else {
42+
next();
43+
}
44+
});
3445

35-
// Mount ObjectQL handler
36-
// Supports both /api/objectql (POST) or similar
46+
// Mount handlers
3747
server.all('/api/objectql', objectQLHandler);
48+
server.all('/api/metadata*', metadataHandler);
49+
server.get('/console*', consoleHandler);
50+
51+
// Create some sample data
52+
const ctx = app.createContext({ isSystem: true });
53+
await ctx.object('User').create({ name: 'Alice', email: 'alice@example.com', age: 28, status: 'active' });
54+
await ctx.object('User').create({ name: 'Bob', email: 'bob@example.com', age: 35, status: 'active' });
55+
await ctx.object('User').create({ name: 'Charlie', email: 'charlie@example.com', age: 42, status: 'inactive' });
56+
57+
await ctx.object('Task').create({ title: 'Complete project', description: 'Finish the ObjectQL console', status: 'in-progress', priority: 'high' });
58+
await ctx.object('Task').create({ title: 'Write documentation', description: 'Document the new console feature', status: 'pending', priority: 'medium' });
59+
await ctx.object('Task').create({ title: 'Code review', description: 'Review pull requests', status: 'pending', priority: 'low' });
60+
await ctx.object('Task').create({ title: 'Deploy to production', description: 'Release version 1.0', status: 'pending', priority: 'high', completed: false });
3861

3962
server.listen(port, () => {
40-
console.log(`Express app listening on port ${port}`);
41-
console.log(`Test CURL:`);
42-
console.log(`curl -X POST http://localhost:${port}/api/objectql -H "Content-Type: application/json" -d '{"op": "create", "object": "User", "args": { "data": { "name": "ExpressUser", "email": "express@example.com" }}}'`);
63+
console.log(`\n🚀 ObjectQL Server running on http://localhost:${port}`);
64+
console.log(`\n📊 Console UI: http://localhost:${port}/console`);
65+
console.log(`\n🔌 API Endpoint: http://localhost:${port}/api/objectql`);
66+
console.log(`\nTest CURL:`);
67+
console.log(`curl -X POST http://localhost:${port}/api/objectql -H "Content-Type: application/json" -d '{"op": "find", "object": "User", "args": {}}'`);
4368
});
4469
}
4570

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Task
2+
label: Tasks
3+
fields:
4+
title:
5+
type: string
6+
label: Title
7+
required: true
8+
description:
9+
type: text
10+
label: Description
11+
status:
12+
type: string
13+
label: Status
14+
defaultValue: pending
15+
priority:
16+
type: string
17+
label: Priority
18+
defaultValue: medium
19+
due_date:
20+
type: date
21+
label: Due Date
22+
completed:
23+
type: boolean
24+
label: Completed
25+
defaultValue: false
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
name: User
2+
label: Users
23
fields:
34
name:
45
type: string
6+
label: Full Name
7+
required: true
58
email:
69
type: string
10+
label: Email Address
11+
required: true
12+
status:
13+
type: string
14+
label: Status
15+
defaultValue: active
16+
age:
17+
type: number
18+
label: Age

examples/integrated-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "dist/index.js",
66
"scripts": {
77
"build": "tsc",
8+
"serve": "objectql serve",
89
"repl": "objectql repl"
910
},
1011
"dependencies": {

packages/cli/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ objectql serve --dir ./src/schema --port 8080
3333
```
3434

3535
The server exposes:
36+
* **Web Console (Swagger UI)**: `http://localhost:<port>/swagger` (GET) - Interactive API explorer.
3637
* **JSON API Endpoint**: `http://localhost:<port>/` (POST)
37-
* **OpenAPI Spec**: `http://localhost:<port>/openapi.json` (GET) - Import this into Postman or Swagger UI.
38+
* **OpenAPI Spec**: `http://localhost:<port>/openapi.json` (GET)
3839

3940
### `repl` (alias: `r`)
4041

packages/cli/src/commands/serve.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@ import { createServer } from 'http';
55
import * as path from 'path';
66
import chalk from 'chalk';
77

8+
const CONSOLE_HTML = `
9+
<!DOCTYPE html>
10+
<html lang="en">
11+
<head>
12+
<meta charset="utf-8" />
13+
<meta name="viewport" content="width=device-width, initial-scale=1" />
14+
<title>ObjectQL Swagger UI</title>
15+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
16+
<style>
17+
body { margin: 0; padding: 0; }
18+
</style>
19+
</head>
20+
<body>
21+
<div id="swagger-ui"></div>
22+
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js" crossorigin></script>
23+
<script>
24+
window.onload = () => {
25+
window.ui = SwaggerUIBundle({
26+
url: '/openapi.json',
27+
dom_id: '#swagger-ui',
28+
});
29+
};
30+
</script>
31+
</body>
32+
</html>
33+
`;
34+
835
export async function serve(options: { port: number; dir: string }) {
936
console.log(chalk.blue('Starting ObjectQL Dev Server...'));
1037

@@ -35,13 +62,31 @@ export async function serve(options: { port: number; dir: string }) {
3562
}
3663

3764
// 3. Create Handler
38-
const handler = createNodeHandler(app);
65+
const internalHandler = createNodeHandler(app);
3966

4067
// 4. Start Server
41-
const server = createServer(handler);
68+
const server = createServer(async (req, res) => {
69+
// Serve Swagger UI
70+
if (req.method === 'GET' && (req.url === '/swagger' || req.url === '/swagger/')) {
71+
res.writeHead(200, { 'Content-Type': 'text/html' });
72+
res.end(CONSOLE_HTML);
73+
return;
74+
}
75+
76+
// Redirect / to /swagger for better DX
77+
if (req.method === 'GET' && req.url === '/') {
78+
res.writeHead(302, { 'Location': '/swagger' });
79+
res.end();
80+
return;
81+
}
82+
83+
// Delegate to API Handler
84+
await internalHandler(req, res);
85+
});
4286

4387
server.listen(options.port, () => {
4488
console.log(chalk.green(`\n🚀 Server ready at http://localhost:${options.port}`));
89+
console.log(chalk.green(`📚 Swagger UI: http://localhost:${options.port}/swagger`));
4590
console.log(chalk.blue(`📖 OpenAPI Spec: http://localhost:${options.port}/openapi.json`));
4691
console.log(chalk.gray('\nTry a curl command:'));
4792
console.log(`curl -X POST http://localhost:${options.port} -H "Content-Type: application/json" -d '{"op": "find", "object": "YourObject", "args": {}}'`);

packages/console/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# @objectql/console
2+
3+
Web-based admin console for ObjectQL database management.
4+
5+
## Features
6+
7+
- **Object Browser**: View all registered objects/tables in your database
8+
- **Data Grid**: Browse and search records with pagination
9+
- **CRUD Operations**: Create, read, update, and delete records
10+
- **Schema Inspector**: View object definitions and field metadata
11+
- **Modern UI**: Clean, responsive design with intuitive navigation
12+
13+
## Usage
14+
15+
The console is typically served alongside your ObjectQL server:
16+
17+
```typescript
18+
import express from 'express';
19+
import { ObjectQL } from '@objectql/core';
20+
import { createNodeHandler } from '@objectql/server';
21+
import { serveConsole } from '@objectql/console/server';
22+
23+
const app = new ObjectQL({ /* ... */ });
24+
const server = express();
25+
26+
// API endpoints
27+
server.all('/api/objectql', createNodeHandler(app));
28+
29+
// Serve console UI
30+
server.use('/console', serveConsole());
31+
32+
server.listen(3004);
33+
```
34+
35+
Then visit `http://localhost:3004/console` in your browser.
36+
37+
## Development
38+
39+
```bash
40+
pnpm install
41+
pnpm run dev
42+
```

packages/console/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>ObjectQL Console</title>
7+
</head>
8+
<body>
9+
<div id="root"></div>
10+
<script type="module" src="/src/main.tsx"></script>
11+
</body>
12+
</html>

packages/console/package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "@objectql/console",
3+
"version": "0.1.0",
4+
"description": "Web-based admin console for ObjectQL database management",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc && vite build",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"react": "^18.2.0",
13+
"react-dom": "^18.2.0",
14+
"react-router-dom": "^6.20.0"
15+
},
16+
"devDependencies": {
17+
"@types/react": "^18.2.43",
18+
"@types/react-dom": "^18.2.17",
19+
"@vitejs/plugin-react": "^4.2.1",
20+
"typescript": "^5.3.0",
21+
"vite": "^5.0.8"
22+
}
23+
}

0 commit comments

Comments
 (0)