database-studio 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Database Studio App 🚀
2
+
3
+ [![Version](https://img.shields.io/badge/version-1.0.1-blue.svg)](./package.json)
4
+ [![License](https://img.shields.io/badge/license-private-red.svg)](#license)
5
+ [![Tech Stack](https://img.shields.io/badge/tech-React%20%7C%20Express%20%7C%20MySQL-orange.svg)](#tech-stack)
6
+
7
+ **Database Studio App** is a high-performance, full-stack database management and visualization tool. Designed for developers and database administrators, it provides a modern, intuitive interface to explore, analyze, and document your MySQL databases with ease.
8
+
9
+ ---
10
+
11
+ ## 🌟 Key Features
12
+
13
+ ### 📊 Interactive Database Viewer
14
+ Explore your data with a high-speed, paginated data table. View real-time records from your MySQL tables with a clean, responsive interface.
15
+
16
+ ### 📐 Visual Schema Explorer
17
+ Visualize your entire database architecture at a glance. Our grid-based **Schema View** transforms complex table relationships into beautiful, interactive cards with dynamic color-coding.
18
+
19
+ ### 📄 Professional PDF Documentation
20
+ Generate high-fidelity database schema reports with a single click. Powered by **Puppeteer** server-side rendering, our exports ensure 100% style accuracy and a professional A4 print layout.
21
+
22
+ ### 🔐 Enterprise-Grade Security
23
+ - **Machine-ID Binding**: Licenses are securely tied to unique hardware identifiers.
24
+ - **JWT Authentication**: Secure session management using JSON Web Tokens and HTTP-only cookies.
25
+ - **RSA-256 Verification**: Offline license verification using public/private key pairs.
26
+
27
+ ### 🛠️ Developer-First Experience
28
+ - **Customizable Layout**: Toggle between 1-4 column grid views.
29
+ - **View Switching**: Seamlessly jump between raw data and architectural schemas.
30
+ - **Fast Search**: Quickly find the tables and columns you need.
31
+
32
+ ---
33
+
34
+ ## 🚀 Quick Start
35
+
36
+ ### Prerequisites
37
+ - **Node.js**: v18 or higher
38
+ - **pnpm**: Recommended package manager
39
+ - **MySQL**: Access to a running MySQL instance
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ # Install dependencies
45
+ pnpm install
46
+
47
+ # Build the application
48
+ pnpm build
49
+ ```
50
+
51
+ ### Environment Configuration
52
+
53
+ Create a `.env` file in the root of the `studio-app` directory:
54
+
55
+ ```env
56
+ MYSQL_URL=mysql://user:password@localhost:3306/your_database
57
+ LICENSE_SERVER_URL=http://your-license-server.com
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 💻 Tech Stack
63
+
64
+ - **Frontend**: [React 18](https://react.dev/), [React Router 6](https://reactrouter.com/), [Tailwind CSS 3](https://tailwindcss.com/)
65
+ - **Backend**: [Express 5](https://expressjs.com/), [Puppeteer](https://pptr.dev/)
66
+ - **UI Components**: [Radix UI](https://www.radix-ui.com/), [Lucide Icons](https://lucide.dev/)
67
+ - **Database**: [MySQL](https://www.mysql.com/)
68
+ - **Build System**: [Vite](https://vitejs.dev/), [tsup](https://tsup.egoist.dev/)
69
+
70
+ ---
71
+
72
+ ## 🏗️ Project Architecture
73
+
74
+ ```text
75
+ client/ # Modern React SPA
76
+ ├── components/ # Reusable UI & Logic components
77
+ ├── pages/ # Route-level components
78
+ └── lib/ # Utilities and API wrappers
79
+
80
+ server/ # Express API & Documentation Engine
81
+ ├── index.ts # Main entry point with License & PDF logic
82
+ └── config.ts # Server configuration
83
+
84
+ bin/ # CLI tools for global linking
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 📑 PDF Export Engine
90
+
91
+ Our export engine doesn't just "print" the page. It uses a sophisticated server-side flow:
92
+ 1. **Headless Navigation**: Puppeteer visits a specialized `?export=true` route.
93
+ 2. **Adaptive CSS**: The app re-renders specifically for print, enforcing a single-column layout and hiding interactive elements.
94
+ 3. **True Fidelity**: Captures full Tailwind colors and styles into a multi-page A4 PDF.
95
+
96
+ ---
97
+
98
+ ## 🔒 License & Security
99
+
100
+ This application requires a valid license key for operation. The licensing system uses **RSA-256** signatures to ensure integrity and **Machine-ID** hashing to prevent unauthorized distribution.
101
+
102
+ ---
103
+
104
+ *Part of the [Database Studio Workspace](../../README.md).*
package/dist/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # Database Studio App 🚀
2
+
3
+ [![Version](https://img.shields.io/badge/version-1.0.1-blue.svg)](./package.json)
4
+ [![License](https://img.shields.io/badge/license-private-red.svg)](#license)
5
+ [![Tech Stack](https://img.shields.io/badge/tech-React%20%7C%20Express%20%7C%20MySQL-orange.svg)](#tech-stack)
6
+
7
+ **Database Studio App** is a high-performance, full-stack database management and visualization tool. Designed for developers and database administrators, it provides a modern, intuitive interface to explore, analyze, and document your MySQL databases with ease.
8
+
9
+ ---
10
+
11
+ ## 🌟 Key Features
12
+
13
+ ### 📊 Interactive Database Viewer
14
+ Explore your data with a high-speed, paginated data table. View real-time records from your MySQL tables with a clean, responsive interface.
15
+
16
+ ### 📐 Visual Schema Explorer
17
+ Visualize your entire database architecture at a glance. Our grid-based **Schema View** transforms complex table relationships into beautiful, interactive cards with dynamic color-coding.
18
+
19
+ ### 📄 Professional PDF Documentation
20
+ Generate high-fidelity database schema reports with a single click. Powered by **Puppeteer** server-side rendering, our exports ensure 100% style accuracy and a professional A4 print layout.
21
+
22
+ ### 🔐 Enterprise-Grade Security
23
+ - **Machine-ID Binding**: Licenses are securely tied to unique hardware identifiers.
24
+ - **JWT Authentication**: Secure session management using JSON Web Tokens and HTTP-only cookies.
25
+ - **RSA-256 Verification**: Offline license verification using public/private key pairs.
26
+
27
+ ### 🛠️ Developer-First Experience
28
+ - **Customizable Layout**: Toggle between 1-4 column grid views.
29
+ - **View Switching**: Seamlessly jump between raw data and architectural schemas.
30
+ - **Fast Search**: Quickly find the tables and columns you need.
31
+
32
+ ---
33
+
34
+ ## 🚀 Quick Start
35
+
36
+ ### Prerequisites
37
+ - **Node.js**: v18 or higher
38
+ - **pnpm**: Recommended package manager
39
+ - **MySQL**: Access to a running MySQL instance
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ # Install dependencies
45
+ pnpm install
46
+
47
+ # Build the application
48
+ pnpm build
49
+ ```
50
+
51
+ ### Environment Configuration
52
+
53
+ Create a `.env` file in the root of the `studio-app` directory:
54
+
55
+ ```env
56
+ MYSQL_URL=mysql://user:password@localhost:3306/your_database
57
+ LICENSE_SERVER_URL=http://your-license-server.com
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 💻 Tech Stack
63
+
64
+ - **Frontend**: [React 18](https://react.dev/), [React Router 6](https://reactrouter.com/), [Tailwind CSS 3](https://tailwindcss.com/)
65
+ - **Backend**: [Express 5](https://expressjs.com/), [Puppeteer](https://pptr.dev/)
66
+ - **UI Components**: [Radix UI](https://www.radix-ui.com/), [Lucide Icons](https://lucide.dev/)
67
+ - **Database**: [MySQL](https://www.mysql.com/)
68
+ - **Build System**: [Vite](https://vitejs.dev/), [tsup](https://tsup.egoist.dev/)
69
+
70
+ ---
71
+
72
+ ## 🏗️ Project Architecture
73
+
74
+ ```text
75
+ client/ # Modern React SPA
76
+ ├── components/ # Reusable UI & Logic components
77
+ ├── pages/ # Route-level components
78
+ └── lib/ # Utilities and API wrappers
79
+
80
+ server/ # Express API & Documentation Engine
81
+ ├── index.ts # Main entry point with License & PDF logic
82
+ └── config.ts # Server configuration
83
+
84
+ bin/ # CLI tools for global linking
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 📑 PDF Export Engine
90
+
91
+ Our export engine doesn't just "print" the page. It uses a sophisticated server-side flow:
92
+ 1. **Headless Navigation**: Puppeteer visits a specialized `?export=true` route.
93
+ 2. **Adaptive CSS**: The app re-renders specifically for print, enforcing a single-column layout and hiding interactive elements.
94
+ 3. **True Fidelity**: Captures full Tailwind colors and styles into a multi-page A4 PDF.
95
+
96
+ ---
97
+
98
+ ## 🔒 License & Security
99
+
100
+ This application requires a valid license key for operation. The licensing system uses **RSA-256** signatures to ensure integrity and **Machine-ID** hashing to prevent unauthorized distribution.
101
+
102
+ ---
103
+
104
+ *Part of the [Database Studio Workspace](../../README.md).*
package/dist/cli.cjs CHANGED
@@ -1,5 +1,14 @@
1
1
  #!/usr/bin/env node
2
- var k=Object.create;var S=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty;var D=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var U=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of P(t))!R.call(e,c)&&c!==a&&S(e,c,{get:()=>t[c],enumerable:!(s=C(t,c))||s.enumerable});return e};var m=(e,t,a)=>(a=e!=null?k(I(e)):{},U(t||!e||!e.__esModule?S(a,"default",{value:e,enumerable:!0}):a,e));var L=D((ie,q)=>{var $=Object.create,y=Object.defineProperty,N=Object.getOwnPropertyDescriptor,A=Object.getOwnPropertyNames,Q=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty,H=(e,t)=>{for(var a in t)y(e,a,{get:t[a],enumerable:!0})},O=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of A(t))!B.call(e,c)&&c!==a&&y(e,c,{get:()=>t[c],enumerable:!(s=N(t,c))||s.enumerable});return e},d=(e,t,a)=>(a=e!=null?$(Q(e)):{},O(t||!e||!e.__esModule?y(a,"default",{value:e,enumerable:!0}):a,e)),Y=e=>O(y({},"__esModule",{value:!0}),e),E={};H(E,{startServer:()=>oe});q.exports=Y(E);var f=d(require("express"),1),h=d(require("path"),1),K=d(require("dotenv"),1),W=d(require("mysql2/promise"),1),z=d(require("puppeteer"),1),G=d(require("axios"),1),V=d(require("cookie-parser"),1),J=d(require("jsonwebtoken"),1),b=d(require("os"),1),X=d(require("crypto"),1),Z=d(require("fs"),1),ee=process.env.LICENSE_SERVER_URL||"http://localhost:4000",te=h.default.resolve(__dirname,"../public.pem"),ae=Z.default.readFileSync(te,"utf8"),re=async(e,t,a)=>{let s=e.cookies?.studio_token;if(!s)return t.status(401).json({success:!1});if(!w(s))return t.status(401).json({success:!1,message:"Invalid license token"});a()};async function oe({port:e}){if(K.default.config({path:h.default.join(process.cwd(),".env")}),!process.env.MYSQL_URL)throw new Error("MYSQL_URL not found in .env");let t=e??5555,a=(0,f.default)();a.use((0,V.default)()),a.use(f.default.json({limit:"50mb"})),a.use(f.default.urlencoded({extended:!0,limit:"50mb"})),a.use("/api",(i,r,o)=>{if(i.path==="/validate-license")return o();re(i,r,o)}),a.post("/api/validate-license",async(i,r)=>{let{licenseKey:o}=i.body;if(!o)return r.status(400).json({success:!1});let l=F();try{let n=await G.default.post(`${ee}/api/validate`,{licenseKey:o,machineId:l},{timeout:3e3}),{token:u}=n.data;if(!w(u))return r.status(401).json({success:!1});r.cookie("studio_token",u,{httpOnly:!0,sameSite:"lax",secure:!1}),r.json({success:!0})}catch(n){if(n?.response?.data?.message)return r.status(401).json({success:!1,message:n?.response?.data?.message});console.error("\u274C Failed to validate license:",n?.response?.data),r.status(401).json({success:!1,message:String(n)})}});let s;try{s=await W.default.createConnection(process.env.MYSQL_URL),console.log("\u{1F50C} Connected to MySQL")}catch(i){throw console.error("\u274C Failed to connect to MySQL"),i}a.get("/api/database",async(i,r)=>{try{let[o]=await s.query("SELECT DATABASE()"),l=o.map(n=>Object.values(n)[0]);r.json({dbname:l[0],success:!0,message:"DB fetched successfully"})}catch(o){r.json({success:!1,message:"Error fetching db name.",error:String(o)})}}),a.get("/api/tables",async(i,r)=>{let[o]=await s.query("SHOW TABLES"),l=o.map(n=>Object.values(n)[0]);r.json(l)}),a.get("/api/table/:name/schema",async(i,r)=>{let o=await j(i.params.name,s);try{let[l]=await s.query(`SHOW COLUMNS FROM \`${o}\``),n=l.map(u=>({name:u.Field,type:u.Type,nullable:u.Null==="YES",key:u.Key,default:u.Default,extra:u.Extra}));r.json({name:o,columns:n})}catch(l){console.error(l),r.status(500).json({error:"Failed to fetch table schema"})}}),a.get("/api/export-pdf",async(i,r)=>{let o=i.cookies?.studio_token;if(!o)return r.status(401).json({error:"Unauthorized"});if(!w(o))return r.status(401).json({error:"Unauthorized"});let l;try{l=await z.default.launch({headless:!0,args:["--no-sandbox","--disable-setuid-sandbox"]});let n=await l.newPage();await n.setCookie({name:"studio_token",value:o,domain:"localhost",path:"/"}),await n.emulateMediaType("print"),await n.addStyleTag({content:`
2
+ var A=Object.create;var S=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var R=Object.getPrototypeOf,x=Object.prototype.hasOwnProperty;var T=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var B=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of P(t))!x.call(e,c)&&c!==a&&S(e,c,{get:()=>t[c],enumerable:!(s=D(t,c))||s.enumerable});return e};var m=(e,t,a)=>(a=e!=null?A(R(e)):{},B(t||!e||!e.__esModule?S(a,"default",{value:e,enumerable:!0}):a,e));var q=T((se,F)=>{var N=Object.create,h=Object.defineProperty,Q=Object.getOwnPropertyDescriptor,Y=Object.getOwnPropertyNames,U=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty,z=(e,t)=>{for(var a in t)h(e,a,{get:t[a],enumerable:!0})},O=(e,t,a,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let c of Y(t))!_.call(e,c)&&c!==a&&h(e,c,{get:()=>t[c],enumerable:!(s=Q(t,c))||s.enumerable});return e},d=(e,t,a)=>(a=e!=null?N(U(e)):{},O(t||!e||!e.__esModule?h(a,"default",{value:e,enumerable:!0}):a,e)),H=e=>O(h({},"__esModule",{value:!0}),e),E={};z(E,{startServer:()=>ae});F.exports=H(E);var f=d(require("express"),1),w=d(require("path"),1),K=d(require("dotenv"),1),W=d(require("mysql2/promise"),1),$=d(require("puppeteer"),1),V=d(require("axios"),1),X=d(require("cookie-parser"),1),Z=d(require("jsonwebtoken"),1),b=d(require("os"),1),G=d(require("crypto"),1),J=process.env.LICENSE_SERVER_URL||"http://localhost:4000",ee=`-----BEGIN PUBLIC KEY-----
3
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNtHPt9FWow0wkX8QszM
4
+ fsS6raS0edOuhkfJYnoMfSHeQ/cBztIV3kTOqu8N2YOzZhPdOFh8b5lBQMyzShXf
5
+ PpvtCjbW0LzkWYiv1ak33RrV9rh2s599toI+sDv/vMv/msB7RHCNEqaIfeeMYdu4
6
+ YrwXOSptGTZPLYe/aitfE4z0ez5P7QR93uZAEftqlODbU/4z4pRcFCwdB2J0+DIR
7
+ Kik1C6Cth/RHAqznVr75VSkiONlX47cFNXny2L4Ujt20wbVyRtErnYdtZDAaVYv7
8
+ ZDHKTqt4L2FyD6WwckqHQwDSUbRjWpMyIwJD7YNg2XSKPldtacuw4VlgD0XaAoM5
9
+ jQIDAQAB
10
+ -----END PUBLIC KEY-----
11
+ `,te=async(e,t,a)=>{let s=e.cookies?.studio_token;if(!s)return t.status(401).json({success:!1});if(!v(s))return t.status(401).json({success:!1,message:"Invalid license token"});a()};async function ae({port:e}){if(K.default.config({path:w.default.join(process.cwd(),".env")}),!process.env.MYSQL_URL)throw new Error("MYSQL_URL not found in .env");let t=e??5555,a=(0,f.default)();a.use((0,X.default)()),a.use(f.default.json({limit:"50mb"})),a.use(f.default.urlencoded({extended:!0,limit:"50mb"})),a.use("/api",(i,r,o)=>{if(i.path==="/validate-license")return o();te(i,r,o)}),a.post("/api/validate-license",async(i,r)=>{let{licenseKey:o}=i.body;if(!o)return r.status(400).json({success:!1});let l=M();try{let n=await V.default.post(`${J}/api/validate`,{licenseKey:o,machineId:l},{timeout:3e3}),{token:u}=n.data;if(!v(u))return r.status(401).json({success:!1});r.cookie("studio_token",u,{httpOnly:!0,sameSite:"lax",secure:!1}),r.json({success:!0})}catch(n){if(n?.response?.data?.message)return r.status(401).json({success:!1,message:n?.response?.data?.message});console.error("\u274C Failed to validate license:",n?.response?.data),r.status(401).json({success:!1,message:String(n)})}});let s;try{s=await W.default.createConnection(process.env.MYSQL_URL),console.log("\u{1F50C} Connected to MySQL")}catch(i){throw console.error("\u274C Failed to connect to MySQL"),i}a.get("/api/database",async(i,r)=>{try{let[o]=await s.query("SELECT DATABASE()"),l=o.map(n=>Object.values(n)[0]);r.json({dbname:l[0],success:!0,message:"DB fetched successfully"})}catch(o){r.json({success:!1,message:"Error fetching db name.",error:String(o)})}}),a.get("/api/tables",async(i,r)=>{let[o]=await s.query("SHOW TABLES"),l=o.map(n=>Object.values(n)[0]);r.json(l)}),a.get("/api/table/:name/schema",async(i,r)=>{let o=await j(i.params.name,s);try{let[l]=await s.query(`SHOW COLUMNS FROM \`${o}\``),n=l.map(u=>({name:u.Field,type:u.Type,nullable:u.Null==="YES",key:u.Key,default:u.Default,extra:u.Extra}));r.json({name:o,columns:n})}catch(l){console.error(l),r.status(500).json({error:"Failed to fetch table schema"})}}),a.get("/api/export-pdf",async(i,r)=>{let o=i.cookies?.studio_token;if(!o)return r.status(401).json({error:"Unauthorized"});if(!v(o))return r.status(401).json({error:"Unauthorized"});let l;try{l=await $.default.launch({headless:!0,args:["--no-sandbox","--disable-setuid-sandbox"]});let n=await l.newPage();await n.setCookie({name:"studio_token",value:o,domain:"localhost",path:"/"}),await n.emulateMediaType("print"),await n.addStyleTag({content:`
3
12
  @media print {
4
13
  html, body {
5
14
  height: auto !important;
@@ -16,5 +25,5 @@ var k=Object.create;var S=Object.defineProperty;var C=Object.getOwnPropertyDescr
16
25
  min-height: auto !important;
17
26
  }
18
27
  }
19
- `}),await n.goto(`http://localhost:${t}?view=schema&export=true`,{waitUntil:"networkidle0"});let u=await n.pdf({format:"A4",printBackground:!0,margin:{top:"10mm",bottom:"10mm",left:"10mm",right:"10mm"},preferCSSPageSize:!0});r.contentType("application/pdf"),r.send(u)}catch(n){console.error("PDF generation error:",n),r.status(500).json({error:"Failed to generate PDF"})}finally{l&&await l.close()}}),a.get("/api/table/:name",async(i,r)=>{let o=await j(i.params.name,s),l=Math.min(parseInt(i.query.limit)||10,1e3),n=Math.max(parseInt(i.query.page)||1,1),u=(n-1)*l;try{let[g]=await s.query(`SELECT * FROM \`${o}\` LIMIT ? OFFSET ?`,[l,u]),[[{total:v}]]=await s.query(`SELECT COUNT(*) as total FROM \`${o}\``),[_]=await s.query(`SHOW COLUMNS FROM \`${o}\``),T=_.map(p=>({name:p.Field,type:p.Type,nullable:p.Null==="YES",key:p.Key,default:p.Default,extra:p.Extra}));r.json({name:o,columns:T,rows:g,total:v,pagination:{total:v,page:n,limit:l,totalPages:Math.ceil(v/l)}})}catch(g){console.error(g),r.status(500).json({error:"Failed to fetch table"})}});let c=h.default.resolve(__dirname,"spa");return console.log("Serving SPA from:",c),a.use(f.default.static(c)),a.get(/.*/,(i,r)=>{r.sendFile(h.default.join(c,"index.html"))}),new Promise(i=>{let r=a.listen(t,()=>{console.log(`\u{1F680} Database Studio running at http://localhost:${t}`),i(r)});process.on("SIGINT",async()=>{console.log(`
20
- \u{1F6D1} Shutting down...`),r.close(async()=>{try{await s.end(),console.log("\u{1F50C} MySQL connection closed")}catch(o){console.error("Error closing MySQL connection:",o)}process.exit(0)})})})}async function j(e,t){let[a]=await t.query("SHOW TABLES");if(!a.map(s=>Object.values(s)[0]).includes(e))throw new Error("Invalid table name");return e}function F(){let e=b.default.networkInterfaces(),t=Object.values(e).flat().find(a=>a&&!a.internal&&a.mac!=="00:00:00:00:00:00")?.mac;return X.default.createHash("sha256").update(t||b.default.hostname()).digest("hex")}function w(e){try{let t=J.default.verify(e,ae,{algorithms:["RS256"],issuer:"database-studio-license",audience:"database-studio-app"}),a=F();return t.machineId!==a?!1:t}catch{return!1}}});var M=m(require("dotenv"),1),x=m(require("path"),1);M.default.config({path:x.default.join(process.cwd(),".env")});async function se(){try{let e=await Promise.resolve().then(()=>m(L(),1));e.startServer||(console.error("\u274C startServer export not found"),process.exit(1));let t=process.env.PORT||"5555";await e.startServer({port:t});let a=`http://localhost:${t}`,s=(await import("open")).default;await s(`http://localhost:${t}`)}catch(e){console.error("\u274C Failed to start Database Studio"),console.error(e),process.exit(1)}}se();
28
+ `}),await n.goto(`http://localhost:${t}?view=schema&export=true`,{waitUntil:"networkidle0"});let u=await n.pdf({format:"A4",printBackground:!0,margin:{top:"10mm",bottom:"10mm",left:"10mm",right:"10mm"},preferCSSPageSize:!0});r.contentType("application/pdf"),r.send(u)}catch(n){console.error("PDF generation error:",n),r.status(500).json({error:"Failed to generate PDF"})}finally{l&&await l.close()}}),a.get("/api/table/:name",async(i,r)=>{let o=await j(i.params.name,s),l=Math.min(parseInt(i.query.limit)||10,1e3),n=Math.max(parseInt(i.query.page)||1,1),u=(n-1)*l;try{let[y]=await s.query(`SELECT * FROM \`${o}\` LIMIT ? OFFSET ?`,[l,u]),[[{total:g}]]=await s.query(`SELECT COUNT(*) as total FROM \`${o}\``),[k]=await s.query(`SHOW COLUMNS FROM \`${o}\``),C=k.map(p=>({name:p.Field,type:p.Type,nullable:p.Null==="YES",key:p.Key,default:p.Default,extra:p.Extra}));r.json({name:o,columns:C,rows:y,total:g,pagination:{total:g,page:n,limit:l,totalPages:Math.ceil(g/l)}})}catch(y){console.error(y),r.status(500).json({error:"Failed to fetch table"})}});let c=w.default.resolve(__dirname,"spa");return console.log("Serving SPA from:",c),a.use(f.default.static(c)),a.get(/.*/,(i,r)=>{r.sendFile(w.default.join(c,"index.html"))}),new Promise(i=>{let r=a.listen(t,()=>{console.log(`\u{1F680} Database Studio running at http://localhost:${t}`),i(r)});process.on("SIGINT",async()=>{console.log(`
29
+ \u{1F6D1} Shutting down...`),r.close(async()=>{try{await s.end(),console.log("\u{1F50C} MySQL connection closed")}catch(o){console.error("Error closing MySQL connection:",o)}process.exit(0)})})})}async function j(e,t){let[a]=await t.query("SHOW TABLES");if(!a.map(s=>Object.values(s)[0]).includes(e))throw new Error("Invalid table name");return e}function M(){let e=b.default.networkInterfaces(),t=Object.values(e).flat().find(a=>a&&!a.internal&&a.mac!=="00:00:00:00:00:00")?.mac;return G.default.createHash("sha256").update(t||b.default.hostname()).digest("hex")}function v(e){try{let t=Z.default.verify(e,ee,{algorithms:["RS256"],issuer:"database-studio-license",audience:"database-studio-app"}),a=M();return t.machineId!==a?!1:t}catch{return!1}}});var I=m(require("dotenv"),1),L=m(require("path"),1);I.default.config({path:L.default.join(process.cwd(),".env")});async function re(){try{let e=await Promise.resolve().then(()=>m(q(),1));e.startServer||(console.error("\u274C startServer export not found"),process.exit(1));let t=process.env.PORT||"5555";await e.startServer({port:t});let a=`http://localhost:${t}`,s=(await import("open")).default;await s(`http://localhost:${t}`)}catch(e){console.error("\u274C Failed to start Database Studio"),console.error(e),process.exit(1)}}re();
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNtHPt9FWow0wkX8QszM
3
+ fsS6raS0edOuhkfJYnoMfSHeQ/cBztIV3kTOqu8N2YOzZhPdOFh8b5lBQMyzShXf
4
+ PpvtCjbW0LzkWYiv1ak33RrV9rh2s599toI+sDv/vMv/msB7RHCNEqaIfeeMYdu4
5
+ YrwXOSptGTZPLYe/aitfE4z0ez5P7QR93uZAEftqlODbU/4z4pRcFCwdB2J0+DIR
6
+ Kik1C6Cth/RHAqznVr75VSkiONlX47cFNXny2L4Ujt20wbVyRtErnYdtZDAaVYv7
7
+ ZDHKTqt4L2FyD6WwckqHQwDSUbRjWpMyIwJD7YNg2XSKPldtacuw4VlgD0XaAoM5
8
+ jQIDAQAB
9
+ -----END PUBLIC KEY-----
@@ -1,4 +1,13 @@
1
- var C=Object.create;var h=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var P=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var q=(a,s)=>{for(var e in s)h(a,e,{get:s[e],enumerable:!0})},b=(a,s,e,c)=>{if(s&&typeof s=="object"||typeof s=="function")for(let u of U(s))!D.call(a,u)&&u!==e&&h(a,u,{get:()=>s[u],enumerable:!(c=N(s,u))||c.enumerable});return a};var m=(a,s,e)=>(e=a!=null?C(P(a)):{},b(s||!a||!a.__esModule?h(e,"default",{value:a,enumerable:!0}):e,a)),A=a=>b(h({},"__esModule",{value:!0}),a);var H={};q(H,{startServer:()=>Y});module.exports=A(H);var f=m(require("express"),1),y=m(require("path"),1),L=m(require("dotenv"),1),j=m(require("mysql2/promise"),1),x=m(require("puppeteer"),1),M=m(require("axios"),1),_=m(require("cookie-parser"),1),R=m(require("jsonwebtoken"),1),S=m(require("os"),1),k=m(require("crypto"),1),F=m(require("fs"),1);var v=process.env.LICENSE_SERVER_URL||"http://localhost:4000";var Q=y.default.resolve(__dirname,"../public.pem"),$=F.default.readFileSync(Q,"utf8"),B=async(a,s,e)=>{let c=a.cookies?.studio_token;if(!c)return s.status(401).json({success:!1});if(!w(c))return s.status(401).json({success:!1,message:"Invalid license token"});e()};async function Y({port:a}){if(L.default.config({path:y.default.join(process.cwd(),".env")}),!process.env.MYSQL_URL)throw new Error("MYSQL_URL not found in .env");let s=a??5555,e=(0,f.default)();e.use((0,_.default)()),e.use(f.default.json({limit:"50mb"})),e.use(f.default.urlencoded({extended:!0,limit:"50mb"})),e.use("/api",(r,t,n)=>{if(r.path==="/validate-license")return n();B(r,t,n)}),e.post("/api/validate-license",async(r,t)=>{let{licenseKey:n}=r.body;if(!n)return t.status(400).json({success:!1});let l=O();try{let o=await M.default.post(`${v}/api/validate`,{licenseKey:n,machineId:l},{timeout:3e3}),{token:i}=o.data;if(!w(i))return t.status(401).json({success:!1});t.cookie("studio_token",i,{httpOnly:!0,sameSite:"lax",secure:!1}),t.json({success:!0})}catch(o){if(o?.response?.data?.message)return t.status(401).json({success:!1,message:o?.response?.data?.message});console.error("\u274C Failed to validate license:",o?.response?.data),t.status(401).json({success:!1,message:String(o)})}});let c;try{c=await j.default.createConnection(process.env.MYSQL_URL),console.log("\u{1F50C} Connected to MySQL")}catch(r){throw console.error("\u274C Failed to connect to MySQL"),r}e.get("/api/database",async(r,t)=>{try{let[n]=await c.query("SELECT DATABASE()"),l=n.map(o=>Object.values(o)[0]);t.json({dbname:l[0],success:!0,message:"DB fetched successfully"})}catch(n){t.json({success:!1,message:"Error fetching db name.",error:String(n)})}}),e.get("/api/tables",async(r,t)=>{let[n]=await c.query("SHOW TABLES"),l=n.map(o=>Object.values(o)[0]);t.json(l)}),e.get("/api/table/:name/schema",async(r,t)=>{let n=await E(r.params.name,c);try{let[l]=await c.query(`SHOW COLUMNS FROM \`${n}\``),o=l.map(i=>({name:i.Field,type:i.Type,nullable:i.Null==="YES",key:i.Key,default:i.Default,extra:i.Extra}));t.json({name:n,columns:o})}catch(l){console.error(l),t.status(500).json({error:"Failed to fetch table schema"})}}),e.get("/api/export-pdf",async(r,t)=>{let n=r.cookies?.studio_token;if(!n)return t.status(401).json({error:"Unauthorized"});if(!w(n))return t.status(401).json({error:"Unauthorized"});let o;try{o=await x.default.launch({headless:!0,args:["--no-sandbox","--disable-setuid-sandbox"]});let i=await o.newPage();await i.setCookie({name:"studio_token",value:n,domain:"localhost",path:"/"}),await i.emulateMediaType("print"),await i.addStyleTag({content:`
1
+ var A=Object.create;var y=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var P=Object.getPrototypeOf,_=Object.prototype.hasOwnProperty;var B=(a,s)=>{for(var e in s)y(a,e,{get:s[e],enumerable:!0})},b=(a,s,e,c)=>{if(s&&typeof s=="object"||typeof s=="function")for(let u of D(s))!_.call(a,u)&&u!==e&&y(a,u,{get:()=>s[u],enumerable:!(c=T(s,u))||c.enumerable});return a};var m=(a,s,e)=>(e=a!=null?A(P(a)):{},b(s||!a||!a.__esModule?y(e,"default",{value:a,enumerable:!0}):e,a)),U=a=>b(y({},"__esModule",{value:!0}),a);var q={};B(q,{startServer:()=>Q});module.exports=U(q);var f=m(require("express"),1),h=m(require("path"),1),I=m(require("dotenv"),1),j=m(require("mysql2/promise"),1),M=m(require("puppeteer"),1),k=m(require("axios"),1),R=m(require("cookie-parser"),1),O=m(require("jsonwebtoken"),1),w=m(require("os"),1),C=m(require("crypto"),1);var E=process.env.LICENSE_SERVER_URL||"http://localhost:4000",v=`-----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNtHPt9FWow0wkX8QszM
3
+ fsS6raS0edOuhkfJYnoMfSHeQ/cBztIV3kTOqu8N2YOzZhPdOFh8b5lBQMyzShXf
4
+ PpvtCjbW0LzkWYiv1ak33RrV9rh2s599toI+sDv/vMv/msB7RHCNEqaIfeeMYdu4
5
+ YrwXOSptGTZPLYe/aitfE4z0ez5P7QR93uZAEftqlODbU/4z4pRcFCwdB2J0+DIR
6
+ Kik1C6Cth/RHAqznVr75VSkiONlX47cFNXny2L4Ujt20wbVyRtErnYdtZDAaVYv7
7
+ ZDHKTqt4L2FyD6WwckqHQwDSUbRjWpMyIwJD7YNg2XSKPldtacuw4VlgD0XaAoM5
8
+ jQIDAQAB
9
+ -----END PUBLIC KEY-----
10
+ `;var Y=async(a,s,e)=>{let c=a.cookies?.studio_token;if(!c)return s.status(401).json({success:!1});if(!S(c))return s.status(401).json({success:!1,message:"Invalid license token"});e()};async function Q({port:a}){if(I.default.config({path:h.default.join(process.cwd(),".env")}),!process.env.MYSQL_URL)throw new Error("MYSQL_URL not found in .env");let s=a??5555,e=(0,f.default)();e.use((0,R.default)()),e.use(f.default.json({limit:"50mb"})),e.use(f.default.urlencoded({extended:!0,limit:"50mb"})),e.use("/api",(r,t,n)=>{if(r.path==="/validate-license")return n();Y(r,t,n)}),e.post("/api/validate-license",async(r,t)=>{let{licenseKey:n}=r.body;if(!n)return t.status(400).json({success:!1});let l=F();try{let o=await k.default.post(`${E}/api/validate`,{licenseKey:n,machineId:l},{timeout:3e3}),{token:i}=o.data;if(!S(i))return t.status(401).json({success:!1});t.cookie("studio_token",i,{httpOnly:!0,sameSite:"lax",secure:!1}),t.json({success:!0})}catch(o){if(o?.response?.data?.message)return t.status(401).json({success:!1,message:o?.response?.data?.message});console.error("\u274C Failed to validate license:",o?.response?.data),t.status(401).json({success:!1,message:String(o)})}});let c;try{c=await j.default.createConnection(process.env.MYSQL_URL),console.log("\u{1F50C} Connected to MySQL")}catch(r){throw console.error("\u274C Failed to connect to MySQL"),r}e.get("/api/database",async(r,t)=>{try{let[n]=await c.query("SELECT DATABASE()"),l=n.map(o=>Object.values(o)[0]);t.json({dbname:l[0],success:!0,message:"DB fetched successfully"})}catch(n){t.json({success:!1,message:"Error fetching db name.",error:String(n)})}}),e.get("/api/tables",async(r,t)=>{let[n]=await c.query("SHOW TABLES"),l=n.map(o=>Object.values(o)[0]);t.json(l)}),e.get("/api/table/:name/schema",async(r,t)=>{let n=await L(r.params.name,c);try{let[l]=await c.query(`SHOW COLUMNS FROM \`${n}\``),o=l.map(i=>({name:i.Field,type:i.Type,nullable:i.Null==="YES",key:i.Key,default:i.Default,extra:i.Extra}));t.json({name:n,columns:o})}catch(l){console.error(l),t.status(500).json({error:"Failed to fetch table schema"})}}),e.get("/api/export-pdf",async(r,t)=>{let n=r.cookies?.studio_token;if(!n)return t.status(401).json({error:"Unauthorized"});if(!S(n))return t.status(401).json({error:"Unauthorized"});let o;try{o=await M.default.launch({headless:!0,args:["--no-sandbox","--disable-setuid-sandbox"]});let i=await o.newPage();await i.setCookie({name:"studio_token",value:n,domain:"localhost",path:"/"}),await i.emulateMediaType("print"),await i.addStyleTag({content:`
2
11
  @media print {
3
12
  html, body {
4
13
  height: auto !important;
@@ -15,5 +24,5 @@ var C=Object.create;var h=Object.defineProperty;var N=Object.getOwnPropertyDescr
15
24
  min-height: auto !important;
16
25
  }
17
26
  }
18
- `}),await i.goto(`http://localhost:${s}?view=schema&export=true`,{waitUntil:"networkidle0"});let p=await i.pdf({format:"A4",printBackground:!0,margin:{top:"10mm",bottom:"10mm",left:"10mm",right:"10mm"},preferCSSPageSize:!0});t.contentType("application/pdf"),t.send(p)}catch(i){console.error("PDF generation error:",i),t.status(500).json({error:"Failed to generate PDF"})}finally{o&&await o.close()}}),e.get("/api/table/:name",async(r,t)=>{let n=await E(r.params.name,c),l=Math.min(parseInt(r.query.limit)||10,1e3),o=Math.max(parseInt(r.query.page)||1,1),i=(o-1)*l;try{let[p]=await c.query(`SELECT * FROM \`${n}\` LIMIT ? OFFSET ?`,[l,i]),[[{total:g}]]=await c.query(`SELECT COUNT(*) as total FROM \`${n}\``),[T]=await c.query(`SHOW COLUMNS FROM \`${n}\``),I=T.map(d=>({name:d.Field,type:d.Type,nullable:d.Null==="YES",key:d.Key,default:d.Default,extra:d.Extra}));t.json({name:n,columns:I,rows:p,total:g,pagination:{total:g,page:o,limit:l,totalPages:Math.ceil(g/l)}})}catch(p){console.error(p),t.status(500).json({error:"Failed to fetch table"})}});let u=y.default.resolve(__dirname,"spa");return console.log("Serving SPA from:",u),e.use(f.default.static(u)),e.get(/.*/,(r,t)=>{t.sendFile(y.default.join(u,"index.html"))}),new Promise(r=>{let t=e.listen(s,()=>{console.log(`\u{1F680} Database Studio running at http://localhost:${s}`),r(t)});process.on("SIGINT",async()=>{console.log(`
19
- \u{1F6D1} Shutting down...`),t.close(async()=>{try{await c.end(),console.log("\u{1F50C} MySQL connection closed")}catch(n){console.error("Error closing MySQL connection:",n)}process.exit(0)})})})}async function E(a,s){let[e]=await s.query("SHOW TABLES");if(!e.map(u=>Object.values(u)[0]).includes(a))throw new Error("Invalid table name");return a}function O(){let a=S.default.networkInterfaces(),s=Object.values(a).flat().find(e=>e&&!e.internal&&e.mac!=="00:00:00:00:00:00")?.mac;return k.default.createHash("sha256").update(s||S.default.hostname()).digest("hex")}function w(a){try{let s=R.default.verify(a,$,{algorithms:["RS256"],issuer:"database-studio-license",audience:"database-studio-app"}),e=O();return s.machineId!==e?!1:s}catch{return!1}}0&&(module.exports={startServer});
27
+ `}),await i.goto(`http://localhost:${s}?view=schema&export=true`,{waitUntil:"networkidle0"});let p=await i.pdf({format:"A4",printBackground:!0,margin:{top:"10mm",bottom:"10mm",left:"10mm",right:"10mm"},preferCSSPageSize:!0});t.contentType("application/pdf"),t.send(p)}catch(i){console.error("PDF generation error:",i),t.status(500).json({error:"Failed to generate PDF"})}finally{o&&await o.close()}}),e.get("/api/table/:name",async(r,t)=>{let n=await L(r.params.name,c),l=Math.min(parseInt(r.query.limit)||10,1e3),o=Math.max(parseInt(r.query.page)||1,1),i=(o-1)*l;try{let[p]=await c.query(`SELECT * FROM \`${n}\` LIMIT ? OFFSET ?`,[l,i]),[[{total:g}]]=await c.query(`SELECT COUNT(*) as total FROM \`${n}\``),[x]=await c.query(`SHOW COLUMNS FROM \`${n}\``),N=x.map(d=>({name:d.Field,type:d.Type,nullable:d.Null==="YES",key:d.Key,default:d.Default,extra:d.Extra}));t.json({name:n,columns:N,rows:p,total:g,pagination:{total:g,page:o,limit:l,totalPages:Math.ceil(g/l)}})}catch(p){console.error(p),t.status(500).json({error:"Failed to fetch table"})}});let u=h.default.resolve(__dirname,"spa");return console.log("Serving SPA from:",u),e.use(f.default.static(u)),e.get(/.*/,(r,t)=>{t.sendFile(h.default.join(u,"index.html"))}),new Promise(r=>{let t=e.listen(s,()=>{console.log(`\u{1F680} Database Studio running at http://localhost:${s}`),r(t)});process.on("SIGINT",async()=>{console.log(`
28
+ \u{1F6D1} Shutting down...`),t.close(async()=>{try{await c.end(),console.log("\u{1F50C} MySQL connection closed")}catch(n){console.error("Error closing MySQL connection:",n)}process.exit(0)})})})}async function L(a,s){let[e]=await s.query("SHOW TABLES");if(!e.map(u=>Object.values(u)[0]).includes(a))throw new Error("Invalid table name");return a}function F(){let a=w.default.networkInterfaces(),s=Object.values(a).flat().find(e=>e&&!e.internal&&e.mac!=="00:00:00:00:00:00")?.mac;return C.default.createHash("sha256").update(s||w.default.hostname()).digest("hex")}function S(a){try{let s=O.default.verify(a,v,{algorithms:["RS256"],issuer:"database-studio-license",audience:"database-studio-app"}),e=F();return s.machineId!==e?!1:s}catch{return!1}}0&&(module.exports={startServer});
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "database-studio",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "private": false,
6
+ "license": "MIT",
6
7
  "bin": {
7
8
  "database-studio": "./dist/cli.cjs"
8
9
  },
@@ -103,7 +104,7 @@
103
104
  "build:client": "vite build",
104
105
  "build:server": "tsup server/index.ts --format cjs --minify --out-dir dist/server --splitting false",
105
106
  "build:cli": "tsup bin/cli.ts --format cjs --minify --out-dir dist --splitting false",
106
- "build": "pnpm build:client && pnpm build:server && pnpm build:cli",
107
+ "build": "pnpm build:client && pnpm build:server && pnpm build:cli && cp README.md dist/README.md",
107
108
  "start": "node dist/cli.cjs",
108
109
  "link:global": "pnpm link --global"
109
110
  }