admindb 1.2.1 → 2.0.0

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.
Files changed (205) hide show
  1. package/README.md +262 -329
  2. package/dist/app.d.ts +25 -4
  3. package/dist/app.js +79 -15
  4. package/dist/app.js.map +1 -1
  5. package/dist/auth/config.d.ts +19 -0
  6. package/dist/auth/config.js +147 -0
  7. package/dist/auth/config.js.map +1 -0
  8. package/dist/auth/crypto.d.ts +27 -0
  9. package/dist/auth/crypto.js +133 -0
  10. package/dist/auth/crypto.js.map +1 -0
  11. package/dist/auth/index.d.ts +5 -0
  12. package/dist/auth/index.js +22 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/middleware.d.ts +14 -0
  15. package/dist/auth/middleware.js +100 -0
  16. package/dist/auth/middleware.js.map +1 -0
  17. package/dist/auth/routes.d.ts +7 -0
  18. package/dist/auth/routes.js +78 -0
  19. package/dist/auth/routes.js.map +1 -0
  20. package/dist/auth/types.d.ts +35 -0
  21. package/dist/auth/types.js +11 -0
  22. package/dist/auth/types.js.map +1 -0
  23. package/dist/cli/args.d.ts +45 -0
  24. package/dist/cli/args.js +328 -0
  25. package/dist/cli/args.js.map +1 -0
  26. package/dist/cli/config.d.ts +56 -0
  27. package/dist/cli/config.js +133 -0
  28. package/dist/cli/config.js.map +1 -0
  29. package/dist/cli/index.d.ts +4 -0
  30. package/dist/cli/index.js +21 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/runner.d.ts +1 -0
  33. package/dist/cli/runner.js +222 -0
  34. package/dist/cli/runner.js.map +1 -0
  35. package/dist/cli.js +2 -127
  36. package/dist/cli.js.map +1 -1
  37. package/dist/data/datasets.d.ts +17 -0
  38. package/dist/data/datasets.js +133 -0
  39. package/dist/data/datasets.js.map +1 -0
  40. package/dist/data/detector.d.ts +30 -0
  41. package/dist/data/detector.js +472 -0
  42. package/dist/data/detector.js.map +1 -0
  43. package/dist/data/engine.d.ts +10 -0
  44. package/dist/data/engine.js +211 -0
  45. package/dist/data/engine.js.map +1 -0
  46. package/dist/data/index.d.ts +5 -0
  47. package/dist/data/index.js +22 -0
  48. package/dist/data/index.js.map +1 -0
  49. package/dist/data/strategies.d.ts +30 -0
  50. package/dist/data/strategies.js +343 -0
  51. package/dist/data/strategies.js.map +1 -0
  52. package/dist/data/types.d.ts +73 -0
  53. package/dist/data/types.js +8 -0
  54. package/dist/data/types.js.map +1 -0
  55. package/dist/db/database.d.ts +14 -146
  56. package/dist/db/database.js +208 -461
  57. package/dist/db/database.js.map +1 -1
  58. package/dist/db/export.d.ts +2 -2
  59. package/dist/db/export.js +7 -2
  60. package/dist/db/export.js.map +1 -1
  61. package/dist/db/filters.d.ts +22 -0
  62. package/dist/db/filters.js +158 -0
  63. package/dist/db/filters.js.map +1 -0
  64. package/dist/db/index.d.ts +8 -0
  65. package/dist/db/index.js +25 -0
  66. package/dist/db/index.js.map +1 -0
  67. package/dist/db/introspection.d.ts +6 -0
  68. package/dist/db/introspection.js +172 -0
  69. package/dist/db/introspection.js.map +1 -0
  70. package/dist/db/manager.d.ts +32 -19
  71. package/dist/db/manager.js +203 -44
  72. package/dist/db/manager.js.map +1 -1
  73. package/dist/db/migrations.d.ts +5 -0
  74. package/dist/db/migrations.js +110 -0
  75. package/dist/db/migrations.js.map +1 -0
  76. package/dist/db/postgres.d.ts +85 -0
  77. package/dist/db/postgres.js +745 -0
  78. package/dist/db/postgres.js.map +1 -0
  79. package/dist/db/types.d.ts +206 -0
  80. package/dist/db/types.js +7 -0
  81. package/dist/db/types.js.map +1 -0
  82. package/dist/index.d.ts +9 -2
  83. package/dist/index.js +46 -4
  84. package/dist/index.js.map +1 -1
  85. package/dist/public/css/app.css +1 -1
  86. package/dist/public/css/input.css +74 -11
  87. package/dist/public/js/app.js +142 -0
  88. package/dist/public/js/browse.js +591 -104
  89. package/dist/public/js/databases.js +105 -13
  90. package/dist/public/js/designer.js +40 -2
  91. package/dist/public/js/forms.js +610 -28
  92. package/dist/public/js/icons.js +51 -0
  93. package/dist/public/js/inspector.js +638 -0
  94. package/dist/public/js/query.js +93 -3
  95. package/dist/public/js/schema.js +170 -52
  96. package/dist/public/js/seed.js +865 -152
  97. package/dist/routes/api/helpers.d.ts +42 -0
  98. package/dist/routes/api/helpers.js +133 -0
  99. package/dist/routes/api/helpers.js.map +1 -0
  100. package/dist/routes/api/import-export.d.ts +3 -0
  101. package/dist/routes/api/import-export.js +85 -0
  102. package/dist/routes/api/import-export.js.map +1 -0
  103. package/dist/routes/api/index.d.ts +4 -0
  104. package/dist/routes/api/index.js +37 -0
  105. package/dist/routes/api/index.js.map +1 -0
  106. package/dist/routes/api/query.d.ts +3 -0
  107. package/dist/routes/api/query.js +76 -0
  108. package/dist/routes/api/query.js.map +1 -0
  109. package/dist/routes/api/rows.d.ts +3 -0
  110. package/dist/routes/api/rows.js +373 -0
  111. package/dist/routes/api/rows.js.map +1 -0
  112. package/dist/routes/api/seed.d.ts +3 -0
  113. package/dist/routes/api/seed.js +82 -0
  114. package/dist/routes/api/seed.js.map +1 -0
  115. package/dist/routes/api/tables.d.ts +3 -0
  116. package/dist/routes/api/tables.js +160 -0
  117. package/dist/routes/api/tables.js.map +1 -0
  118. package/dist/routes/databases.d.ts +8 -11
  119. package/dist/routes/databases.js +100 -66
  120. package/dist/routes/databases.js.map +1 -1
  121. package/dist/routes/index.d.ts +3 -0
  122. package/dist/routes/index.js +20 -0
  123. package/dist/routes/index.js.map +1 -0
  124. package/dist/routes/pages.d.ts +3 -3
  125. package/dist/routes/pages.js +147 -62
  126. package/dist/routes/pages.js.map +1 -1
  127. package/dist/serverless.d.ts +40 -0
  128. package/dist/serverless.js +210 -0
  129. package/dist/serverless.js.map +1 -0
  130. package/dist/sql/generator.d.ts +4 -3
  131. package/dist/sql/generator.js +103 -6
  132. package/dist/sql/generator.js.map +1 -1
  133. package/dist/sql/index.d.ts +2 -0
  134. package/dist/sql/index.js +19 -0
  135. package/dist/sql/index.js.map +1 -0
  136. package/dist/types/api.d.ts +412 -0
  137. package/dist/types/api.js +9 -0
  138. package/dist/types/api.js.map +1 -0
  139. package/dist/utils/colors.d.ts +40 -0
  140. package/dist/utils/colors.js +64 -0
  141. package/dist/utils/colors.js.map +1 -0
  142. package/dist/{util.d.ts → utils/common.d.ts} +10 -6
  143. package/dist/{util.js → utils/common.js} +80 -13
  144. package/dist/utils/common.js.map +1 -0
  145. package/dist/utils/csv.js.map +1 -0
  146. package/dist/utils/datatype.d.ts +49 -0
  147. package/dist/utils/datatype.js +222 -0
  148. package/dist/utils/datatype.js.map +1 -0
  149. package/dist/utils/icons.d.ts +11 -0
  150. package/dist/utils/icons.js +72 -0
  151. package/dist/utils/icons.js.map +1 -0
  152. package/dist/utils/index.d.ts +4 -0
  153. package/dist/utils/index.js +21 -0
  154. package/dist/utils/index.js.map +1 -0
  155. package/dist/{logger.d.ts → utils/logger.d.ts} +1 -1
  156. package/dist/utils/logger.js +40 -0
  157. package/dist/utils/logger.js.map +1 -0
  158. package/dist/views/layouts/main.hbs +33 -11
  159. package/dist/views/pages/databases.hbs +182 -74
  160. package/dist/views/pages/designer.hbs +9 -6
  161. package/dist/views/pages/error.hbs +3 -3
  162. package/dist/views/pages/form.hbs +5 -3
  163. package/dist/views/pages/home.hbs +22 -13
  164. package/dist/views/pages/login.hbs +111 -0
  165. package/dist/views/pages/query.hbs +8 -6
  166. package/dist/views/pages/schema.hbs +41 -13
  167. package/dist/views/pages/seed.hbs +200 -54
  168. package/dist/views/pages/table.hbs +152 -72
  169. package/dist/views/partials/icon.hbs +2 -0
  170. package/dist/views/partials/navbar.hbs +69 -25
  171. package/dist/views/partials/sidebar.hbs +108 -94
  172. package/package.json +11 -4
  173. package/dist/args.d.ts +0 -28
  174. package/dist/args.js +0 -187
  175. package/dist/args.js.map +0 -1
  176. package/dist/config.d.ts +0 -17
  177. package/dist/config.js +0 -23
  178. package/dist/config.js.map +0 -1
  179. package/dist/csv.js.map +0 -1
  180. package/dist/data/generator.d.ts +0 -86
  181. package/dist/data/generator.js +0 -1031
  182. package/dist/data/generator.js.map +0 -1
  183. package/dist/logger.js +0 -25
  184. package/dist/logger.js.map +0 -1
  185. package/dist/routes/api.d.ts +0 -9
  186. package/dist/routes/api.js +0 -848
  187. package/dist/routes/api.js.map +0 -1
  188. package/dist/test/classifier.test.d.ts +0 -1
  189. package/dist/test/classifier.test.js +0 -44
  190. package/dist/test/classifier.test.js.map +0 -1
  191. package/dist/test/csv.test.d.ts +0 -1
  192. package/dist/test/csv.test.js +0 -64
  193. package/dist/test/csv.test.js.map +0 -1
  194. package/dist/test/database.test.d.ts +0 -1
  195. package/dist/test/database.test.js +0 -402
  196. package/dist/test/database.test.js.map +0 -1
  197. package/dist/test/generator.test.d.ts +0 -1
  198. package/dist/test/generator.test.js +0 -132
  199. package/dist/test/generator.test.js.map +0 -1
  200. package/dist/test/manager.test.d.ts +0 -1
  201. package/dist/test/manager.test.js +0 -146
  202. package/dist/test/manager.test.js.map +0 -1
  203. package/dist/util.js.map +0 -1
  204. /package/dist/{csv.d.ts → utils/csv.d.ts} +0 -0
  205. /package/dist/{csv.js → utils/csv.js} +0 -0
package/README.md CHANGED
@@ -6,96 +6,151 @@
6
6
  [![Downloads](https://img.shields.io/npm/dm/admindb.svg)](https://www.npmjs.com/package/admindb)
7
7
  [![Publish](https://github.com/MIbnEKhalid/admindb/actions/workflows/publish.yml/badge.svg?branch=main)](https://github.com/MIbnEKhalid/admindb/actions/workflows/publish.yml)
8
8
 
9
- **A browser-based SQLite administration tool.** Manage a SQLite database
10
- entirely from the browser — browse and edit rows, run SQL, design schemas,
11
- import/export and seed data — with no separate frontend app to build or deploy.
12
-
13
- - **No frontend build step.** Handlebars server-rendered UI + vanilla JS +
14
- Tailwind/DaisyUI; one Express process serves pages, static assets, and the
15
- JSON API.
16
- - **Standalone or embeddable.** Run it from the CLI against a file or a folder
17
- of databases, or mount it inside an existing Express app under any path on
18
- the same port.
19
- - **Safe SQL by construction.** Every identifier is quoted and every literal is
20
- escaped when SQL is generated, and the "Get query / preview" modes only
21
- produce SQL strings — they never execute.
9
+ **A modern, browser-based SQLite and PostgreSQL database administration tool.** Manage SQLite and PostgreSQL databases entirely from your browser — browse and edit rows, run arbitrary SQL queries, design schemas visually, seed realistic test data, inspect complex data types, and import/export CSV/JSON — with zero frontend build step.
10
+
11
+ - **SQLite & PostgreSQL Multi-Engine:** Seamlessly manage local SQLite files, remote PostgreSQL connections, or multi-database environments with distinct engine badges and credentials protection.
12
+ - **Secure JSON Config Files:** Pass database credentials securely in `.json` files (`name.postgres.json`) without exposing secrets on the CLI.
13
+ - **Zero frontend build step:** Server-rendered Handlebars UI + vanilla JS + Tailwind/DaisyUI; a single lightweight Express process serves pages, static assets, and the REST API.
14
+ - **Standalone CLI or embeddable library:** Run instantly via `npx admindb` or mount it directly into your existing Express application under any subpath.
15
+ - **Modern terminal experience:** Clean, colorized startup banner with auto-detected local/network URLs and streamlined runtime logs.
16
+ - **Rich Data Types & Calendar Controls:** In-place calendar pickers with presets (`Now`, `Yesterday`, `Tomorrow`, `+7 Days`, `+30 Days`), PostgreSQL Array chip managers, JSON modal inspector, UUID generators, and byte dump inspector.
17
+ - **Safe SQL by construction:** Quoted identifiers, escaped literals, parameterized queries, and non-executing SQL preview modes.
18
+
19
+ ---
22
20
 
23
21
  ![Home dashboard](docs/screenshots/home.png)
24
22
 
25
- > More screenshots in [`docs/screenshots/`](docs/screenshots/): table browser,
26
- > query editor, table designer, insert/edit form, schema editor, and the
27
- > databases landing page.
23
+ > 📸 **Visual Tour:** See [`docs/screenshots/`](docs/screenshots/) for screenshots of the Table Browser, Query Editor, Visual Schema Designer, Inline Grid Editor, and Multi-Database Manager.
24
+
25
+ ---
28
26
 
29
- ## 30-Second Start
27
+ ## ⚡ 30-Second Quickstart
30
28
 
31
- No install needed:
29
+ No installation required:
32
30
 
33
31
  ```bash
34
32
  npx admindb
35
- # → AdminDB is listening on http://localhost:3000
36
33
  ```
37
34
 
38
- With no arguments the server starts in **manager mode**: a **Databases** landing
39
- page where you can browse the filesystem and open any SQLite database file
40
- (`.db` / `.sqlite` / `.sqlite3`), or create new ones. Or point it straight at a
41
- file:
35
+ By default, AdminDB opens in **Manager Mode** on `http://localhost:45531`, allowing you to browse the filesystem, create new SQLite databases, or open existing `.db` / `.sqlite` / `.sqlite3` files.
42
36
 
43
- ```bash
44
- npx admindb ./data/app.db # open one database
45
- npx admindb -d ./dbs # manage a folder of databases
46
- ```
37
+ ### 1. Load database connections securely from a JSON file:
47
38
 
48
- From a clone:
39
+ Create `name.postgres.json`:
40
+ ```json
41
+ {
42
+ "prod": "postgresql://postgres:secret@localhost:5432/prod_db",
43
+ "staging": "postgresql://postgres:secret@localhost:5432/staging_db",
44
+ "local": "./data/local.db"
45
+ }
46
+ ```
49
47
 
48
+ Run:
50
49
  ```bash
51
- git clone https://github.com/MIbnEKhalid/admindb.git
52
- cd admindb
53
- npm install
54
- npm run build
55
- npm start # serves http://localhost:3000
50
+ npx admindb name.postgres.json
56
51
  ```
57
52
 
58
- > Requires **Node.js ≥ 20**. Runtime dependencies are `express`,
59
- > `express-handlebars`, and `better-sqlite3`; the rest is the application.
60
-
61
- ## Why This Exists
62
-
63
- AdminDB exists because SQLite deserves a proper web admin UI, and desktop tools
64
- (DBeaver, DB Browser for SQLite) live outside both your browser and your stack.
65
- Web admin tools like phpMyAdmin target MySQL/Postgres, not SQLite. This one is
66
- server-rendered, so there is no frontend build step and nothing extra to deploy
67
- — the same Express process serves pages, assets, and the JSON API. Run it
68
- standalone from the CLI, or mount it under any path of an existing Express app
69
- on the same port.
70
-
71
- ## Install
72
-
73
- ### Try it (no install)
53
+ ### 2. Point directly to a database file or PostgreSQL URI:
74
54
 
75
55
  ```bash
76
- npx admindb -p 8080 # run on port 8080 without installing
77
- npx admindb ./app.db # open a database file directly
56
+ npx admindb ./data/app.db # Open a single SQLite database directly
57
+ npx admindb postgresql://postgres:secret@localhost:5432/mydb # Open a PostgreSQL database directly
58
+ npx admindb -d ./databases # Manage a folder of SQLite databases
59
+ npx admindb -p 8080 -r # Run on port 8080 in read-only mode
78
60
  ```
79
61
 
80
- ### Install as a standalone CLI tool
81
-
82
- Requires **Node.js ≥ 20**:
62
+ ### Install globally:
83
63
 
84
64
  ```bash
85
65
  npm install -g admindb
86
- admindb # starts the server → open http://localhost:3000
66
+ admindb
87
67
  ```
88
68
 
89
- The `admindb` command starts the built-in server and prints the URL to open in
90
- your browser. See the [CLI reference](#cli-reference) for `--port`,
91
- `--open <file>`, `--dir <folder>`, `--readonly`, and more.
69
+ ---
70
+
71
+ ## 🖥️ Modern Terminal Experience
92
72
 
93
- ### Embed AdminDB in your own app
73
+ AdminDB features a clean, colorized CLI startup banner and streamlined, low-noise runtime logging:
94
74
 
95
- AdminDB is an Express app you can mount inside your own application, under your
96
- own path, on the same port as the rest of your server.
75
+ ```text
76
+ ⚡ AdminDB v2.0.0
77
+
78
+ ➜ Local: http://localhost:45531/
79
+ ➜ Network: http://192.168.1.15:45531/
80
+ ➜ Mode: Manager
81
+ ➜ Config: name.postgres.json (2 connection(s))
82
+ • prod: PostgreSQL postgresql://postgres:****@localhost:5432/prod_db
83
+ • staging: PostgreSQL postgresql://postgres:****@localhost:5432/staging_db
84
+ ➜ Auth: User: admin (default password)
85
+
86
+ ⚠ Default password in use (admin). Generate a secure hash with:
87
+ npm run generatehash and set ADMINDB_PASSWORD or -P <hash>
88
+ ```
97
89
 
98
- #### Minimal example
90
+ Runtime operations produce crisp, color-coded status logs:
91
+
92
+ ```text
93
+ 16:38:13 [info] Initialized PostgreSQL pool for postgresql://postgres:****@localhost:5432/prod_db
94
+ 16:38:15 [info] Executed query in 2.4ms (42 rows returned)
95
+ 16:38:18 [warn] Failed login attempt for user "unknown"
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 🌟 Core Features
101
+
102
+ ### 🔍 Browse & Edit Rows
103
+ * **Table Browser:** High-density compact grid by default, column-header sorting, sticky headers, and pinned right-aligned action columns. Composite primary keys are fully supported.
104
+ * **Spreadsheet-Style Inline Editing & Keyboard Navigation:**
105
+ * **Full Grid Navigation:** Navigate cells with <kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd> <kbd>→</kbd> or <kbd>Tab</kbd> / <kbd>Shift+Tab</kbd>.
106
+ * **In-Place Type-Aware Controls:** Double-click or press <kbd>Enter</kbd> to edit in place (FK dropdowns, boolean toggles, date/time pickers with instant calendar triggers, array tags, numeric inputs). Pressing <kbd>Enter</kbd> commits and shifts focus to the cell below. Pressing <kbd>Space</kbd> on boolean cells toggles immediately.
107
+ * **Interactive Date & Time Presets:** Calendar widget with quick shortcuts (`Now / Today`, `Yesterday`, `Tomorrow`, `+7 Days`, `+30 Days`, `Start of Day`, `End of Day`, `Clear`).
108
+ * **PostgreSQL Array Tag Manager:** Interactive chip manager with Enter key chip addition, removal, and `{item1,item2}` array serialization.
109
+ * **Quick Copy Shortcut:** Press <kbd>Ctrl+C</kbd> / <kbd>Cmd+C</kbd> on any focused cell to copy its raw value to the clipboard.
110
+ * **Granular Staging & Single-Cell Revert:** Staged edits are marked with amber indicators (`.cell-dirty`). Hovering reveals an individual undo button (`↺`) to revert a single field without losing the rest of your pending batch.
111
+ * **Staged Changes Diff & Review Drawer:** Floating dock displays pending edit count; click **Review Diff** to inspect a side-by-side comparison of original vs staged values across all modified rows before applying atomically in a single transaction.
112
+ * **Universal Data Inspector:** Rich interactive modal for deep data inspection:
113
+ * **JSON / JSONB Viewer & Editor:** Interactive syntax-highlighted tree viewer, expandable nodes, real-time JSON editor, and format **Beautify** & **Minify** tools.
114
+ * **BLOB / BYTEA & Media Previews:** Automatic MIME sniffing (PNG, JPEG, WebP, GIF, SVG, PDF, audio/video), inline image thumbnails, direct binary download, and drag-and-drop file upload. Supports PostgreSQL `\x...` and `0x...` hex strings.
115
+ * **3-Column Hex Dump:** Professional byte offset, hexadecimal, and printable ASCII viewer for raw binary blobs.
116
+ * **Text & Code Inspector:** Full-height editor for lengthy text fields, SQL strings, markdown, UUIDs, and config blobs with copy shortcuts.
117
+ * **Row Quick Actions:** 3-dots dropdown menu on each row for *Edit*, *Duplicate Row*, *Copy as JSON*, *Copy SQL INSERT*, and *Delete Row*.
118
+ * **Type-Aware Filters:** Filter by exact match, comparison (`>5`, `<=10`), prefix (`pre*`), substring, boolean state, or date/numeric ranges.
119
+ * **Bulk Operations:** Select rows to delete in one transaction (with foreign-key impact previews) or export selected rows as CSV/JSON.
120
+ * **Related Rows:** Cross-table foreign key indicators show how many child records reference each row, with one-click nested table exploration.
121
+
122
+ ### ⚡ Query Runner & SQL Tools
123
+ * **Arbitrary SQL Runner:** Execute queries with results formatted as clean tables; `COUNT` queries display a concise summary, and mutations report affected row counts. Double-click or click inspect on any cell in query results to open the universal inspector.
124
+ * **Saved Named Queries:** Save frequently used queries in the database and reload them from a dropdown menu.
125
+ * **Safe SQL Preview:** Generate `CREATE`, `INSERT`, or `UPDATE` SQL without executing it.
126
+ * **Full Database Dump:** Download the entire database as a standard SQL file (`CREATE TABLE` + `INSERT` statements).
127
+
128
+ ### 🗂️ Visual Schema Designer & Indexes
129
+ * **Visual Table Designer:** Create tables interactively with column types (including `UUID`, `JSONB`, `TIMESTAMP`, `TIMESTAMPTZ`, `INTERVAL`, `BYTEA`, `INET`, `SERIAL`, `BIGINT`), primary keys, autoincrement, nullable/unique constraints, default values, and foreign keys.
130
+ * **Relationship-Safe Schema Editor:** Rename tables, add columns, modify column types, rename columns, and drop columns/tables with safety checks to protect active foreign keys and unique constraints.
131
+ * **Index Manager:** Create single or multi-column indexes (plain or unique) with live SQL previews, and drop existing indexes safely.
132
+
133
+ ### 🔄 Import, Export & Seed Data Generation
134
+ * **CSV Import:** Upload or paste CSV files with column matching, executed transactionally.
135
+ * **Data Export:** Download table data or arbitrary SQL query results as CSV or JSON.
136
+ * **Intelligent Seed Generator:** Populate tables with up to 5,000 realistic rows using intelligent heuristic strategy detection (names, emails, phones, addresses, dates, UUIDs, custom templates, or sampled foreign keys). Includes live table preview before execution.
137
+
138
+ ### 📁 Multi-Database Manager
139
+ * Manage directories of SQLite files, explicit file lists, or named JSON connections.
140
+ * Dedicated landing page with engine badges (`PostgreSQL` / `SQLite`), table counts, connection paths, and seamless database switching.
141
+
142
+ ### 🛡️ Strict Read-Only & Serverless Mode
143
+ * **Serverless Ready:** Auto-detects ephemeral serverless environments (Vercel, AWS Lambda, Cloudflare Pages, Netlify, GCP Cloud Functions).
144
+ * **Smart Serverless Editability Rule:**
145
+ * **SQLite** defaults to **read-only** in serverless mode to prevent data loss on ephemeral filesystems.
146
+ * **PostgreSQL** is **fully editable and writable** in serverless mode because it connects to persistent remote database services.
147
+ * Includes ready-to-use `createServerlessHandler` and `createLambdaHandler` wrappers.
148
+
149
+ ---
150
+
151
+ ## 🚀 Embed AdminDB in Express
152
+
153
+ AdminDB can be mounted directly into any existing Express application under any subpath on the same port:
99
154
 
100
155
  ```ts
101
156
  import express from 'express';
@@ -103,300 +158,178 @@ import { createRouter } from 'admindb';
103
158
 
104
159
  const app = express();
105
160
 
106
- app.get('/', (_req, res) => res.send('My main app'));
161
+ app.get('/', (_req, res) => res.send('Main App'));
162
+
163
+ // Mount AdminDB for SQLite
164
+ app.use('/admin', createRouter({
165
+ dbPath: './data/app.db',
166
+ basePath: '/admin',
167
+ }));
107
168
 
108
- // All AdminDB routes live under /admin on the same port.
109
- app.use('/admin', createRouter({ dbPath: '/data/my.db', basePath: '/admin' }));
169
+ // Or mount AdminDB for PostgreSQL
170
+ app.use('/admin-pg', createRouter({
171
+ connection: 'postgresql://postgres:secret@localhost:5432/mydb',
172
+ basePath: '/admin-pg',
173
+ }));
110
174
 
111
- app.listen(3000);
175
+ app.listen(45531, () => {
176
+ console.log('App running on http://localhost:45531 (Admin: http://localhost:45531/admin)');
177
+ });
112
178
  ```
113
179
 
114
- `createRouter(options)` returns a fully wired Express app (pages + JSON API +
115
- static assets + view engine). Mounting it is just `app.use('/path', router)`.
180
+ > 📖 **Full Options & Advanced Embedding Recipes:**
181
+ > See [**`docs/EXAMPLES.md`**](docs/EXAMPLES.md#-part-2-programmatic-code-examples-express--typescript) for the complete `createRouter` options reference, multi-database management (`DbManager`), custom loggers, read-only mode, and custom authentication configurations.
182
+
183
+ ---
184
+
185
+ ## ⚙️ CLI & Environment Variables
186
+
187
+ Every setting can be configured via **CLI flags**, **Environment Variables**, or **JSON Configuration Files** (CLI flags override JSON config, which overrides environment variables):
188
+
189
+ | Setting | CLI Flag & Aliases | Environment Variable & Aliases | Default | Description |
190
+ | :--- | :--- | :--- | :--- | :--- |
191
+ | **Config File** | `-C, --config <file.json>` | `ADMINDB_CONFIG` | — | Path to a JSON configuration file containing database credentials & settings |
192
+ | **Port** | `-p, --port <port>` | `PORT`, `ADMINDB_PORT` | `45531` | Port to listen on |
193
+ | **Host** | `-H, --host <host>` | `HOST`, `ADMINDB_HOST` | `0.0.0.0` | Host / interface to bind |
194
+ | **Connection URI** | `-c, --connection, --pg <uri>` | `DATABASE_URL`, `ADMINDB_CONNECTION`, `PG_CONNECTION` | — | PostgreSQL connection URI or path |
195
+ | **Single DB** | `-o, --open, --db-path <file>` | `DB_PATH`, `ADMINDB_DB_PATH`, `ADMINDB_PATH` | — | Open a single database file directly |
196
+ | **Database Dir** | `-d, --dir, --db-dir <dir>` | `DB_DIR`, `ADMINDB_DB_DIR`, `ADMINDB_DIR` | — | Folder of database files to manage |
197
+ | **Explicit Files** | `--files, --db-files <list>` | `DB_FILES`, `ADMINDB_DB_FILES` | — | Comma-separated database file paths |
198
+ | **Base Path** | `-b, --base-path, --base <p>` | `BASE_PATH`, `ADMINDB_BASE_PATH` | `''` (`/`) | URL prefix to serve under (e.g. `/admin`) |
199
+ | **Read-Only** | `-r, --readonly, --read-only`| `READONLY`, `ADMINDB_READONLY` | `false` | Open databases read-only (writes disabled) |
200
+ | **Serverless**| `--serverless` | `SERVERLESS`, `ADMINDB_SERVERLESS` | `false` *(auto)* | Serverless mode (SQLite read-only, Postgres editable) |
201
+ | **Auth** | `--auth` / `--no-auth` | `ADMINDB_AUTH`, `ADMINDB_NO_AUTH` | `true` | Enable or disable built-in authentication |
202
+ | **Username** | `-u, --username, --user <user>` | `ADMINDB_USERNAME`, `ADMINDB_USER` | `admin` | Admin username |
203
+ | **Password** | `-P, --password, --pass <pass>` | `ADMINDB_PASSWORD`, `ADMINDB_PASS` | `admin` *(hash)* | Admin password or salted `scrypt:...` hash |
204
+ | **Session Secret**| `--auth-secret, --secret <sec>` | `ADMINDB_SECRET`, `SESSION_SECRET` | *(auto)* | Secret key for signing session cookies |
205
+ | **Log Level** | `-l, --log-level <level>` | `LOG_LEVEL`, `ADMINDB_LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` |
206
+ | **Help** | `-h, --help` | — | — | Show CLI help |
207
+ | **Version** | `-v, --version` | — | — | Show version |
208
+
209
+ Quick CLI examples:
116
210
 
117
- #### Options
211
+ ```bash
212
+ admindb name.postgres.json # Load credentials from JSON file
213
+ admindb postgresql://user:pass@host:5432/db # Open PostgreSQL database
214
+ admindb ./data/app.db # Open SQLite database
215
+ admindb -d ./databases # Manage a folder of databases
216
+ admindb -p 8080 # Run on port 8080
217
+ admindb --no-auth # Authentication disabled
218
+ ```
118
219
 
119
- | Option | Type | Description |
120
- | ---------- | -------------------- | -------------------------------------------------------------- |
121
- | `dbPath` | `string` | Path to a single SQLite file (single-db mode). Default: `admindb.db` |
122
- | `db` | `SqliteDatabase` | An already-open database instance (advanced embedding) |
123
- | `manager` | `DbManager` | Enables multi-database mode (see below) |
124
- | `basePath` | `string` | URL prefix used by templates/assets (e.g. `/admin`). Pass the same prefix you mount at |
125
- | `logger` | `Logger` | Custom logger (see `createLogger`) |
126
- | `logLevel` | `'debug'\|'info'\|'warn'\|'error'` | Log verbosity (used when no logger is passed) |
127
- | `allowBrowse` | `boolean` | Manager mode: show the filesystem file-browser on the databases page. Set `false` to disable it (e.g. when the server was started with specific database files). Default: `true` |
128
- | `browseRoot` | `string` | Manager mode: restrict the file-browser to this folder (absolute path) — it cannot navigate above it and only databases inside it can be opened |
129
- | `readonly` | `boolean` | Open the database(s) **read-only**: every write is rejected (403), write controls are disabled in the UI, and a banner is shown. The DB file is opened with `SQLITE_OPEN_READONLY` + `PRAGMA query_only` as a belt-and-suspenders guard. Default: `false` |
220
+ > 📖 **Full Configuration Reference & Deployment Recipes:**
221
+ > See [**`docs/EXAMPLES.md`**](docs/EXAMPLES.md) for detailed variable explanations, reasons/use cases, and ready-to-run recipes for Bash, PowerShell, Docker, Docker Compose, and Nginx.
130
222
 
131
- Example with a custom logger and prefix:
223
+ ---
132
224
 
133
- ```ts
134
- import express from 'express';
135
- import { createRouter, createLogger } from 'admindb';
225
+ ## 🔒 Authentication & Security
136
226
 
137
- const app = express();
138
- app.use('/tools/db', createRouter({
139
- dbPath: './data/app.db',
140
- basePath: '/tools/db',
141
- logLevel: 'info',
142
- }));
143
- app.listen(3000);
227
+ > [!WARNING]
228
+ > **Important Security Notice:**
229
+ > AdminDB's built-in native authentication provides **basic single-user access control** for local development and private internal tools.
230
+ > For production environments and internet-facing networks, **it is entirely the user's responsibility to protect AdminDB** by placing it behind your own web application's authentication (e.g. NextAuth, Passport, OAuth2/OIDC middleware), an IP-restricted VPN, or a secure reverse proxy with TLS/HTTPS.
231
+
232
+ ### Generate a Secure Password Hash
233
+
234
+ To configure custom credentials with a salted cryptographic `scrypt` hash:
235
+
236
+ ```bash
237
+ npm run generatehash
238
+ ```
239
+
240
+ Paste the resulting hash into `ADMINDB_PASSWORD`, CLI `-P`, or your Express configuration:
241
+
242
+ ```bash
243
+ ADMINDB_USERNAME="ops" ADMINDB_PASSWORD="scrypt:8011bcda...:85465796..." npx admindb
144
244
  ```
145
245
 
146
- #### Multiple databases
246
+ ### Wrapping with Your Own Express Authentication (Recommended for Production)
147
247
 
148
- Pass a `DbManager` to manage several database files — either from a directory,
149
- from an explicit list of file paths, or both:
248
+ When embedding AdminDB in your Express application, turn off built-in auth (`auth: false`) and protect the route with your existing auth middleware:
150
249
 
151
250
  ```ts
152
251
  import express from 'express';
153
- import { createRouter, DbManager, createLogger } from 'admindb';
252
+ import { createRouter } from 'admindb';
154
253
 
155
254
  const app = express();
156
- app.use('/admin', createRouter({
157
- manager: new DbManager(
158
- {
159
- dir: './data', // scan a directory
160
- files: ['/srv/legacy/app.db', './shared.sqlite'], // and/or explicit paths
161
- readonly: true, // open all databases read-only
162
- },
163
- createLogger('info'),
164
- ),
255
+
256
+ app.use('/admin', requireYourAppAuth, createRouter({
257
+ connection: process.env.DATABASE_URL,
165
258
  basePath: '/admin',
259
+ auth: false, // Turn off built-in login form; rely on requireYourAppAuth
166
260
  }));
261
+
262
+ app.listen(45531);
167
263
  ```
168
264
 
169
- In multi-db mode:
170
-
171
- - a **"Databases" landing page** lets you open, create, and delete database files,
172
- - every database is scoped under its file name, e.g.
173
- `/admin/app.db/tables/users` and `/admin/app.db/api/tables`,
174
- - file names that collide across sources are deduped (`name__2.db`).
175
-
176
- ### CLI reference
177
-
178
- The standalone server is a full web app. With no arguments it runs in
179
- **manager mode**: a **Databases** landing page where you can browse the
180
- filesystem and open any SQLite database file (`.db` / `.sqlite` / `.sqlite3`),
181
- or create new ones.
182
-
183
- | Flag | Description |
184
- | -------------------- | -------------------------------------------------------- |
185
- | `-p, --port <port>` | Port to listen on (default `3000`) |
186
- | `-H, --host <host>` | Host / interface to bind (default `0.0.0.0`) |
187
- | `-o, --open <file>` | Open a single database file directly |
188
- | `-d, --dir <dir>` | Manage a folder of database files |
189
- | `--files <list>` | Comma-separated database file paths to manage |
190
- | `-b, --base-path <p>`| URL prefix to serve under (default `/`) |
191
- | `-r, --readonly` | Open databases read-only (all writes disabled) |
192
- | `-l, --log-level <l>`| `debug` \| `info` \| `warn` \| `error` (default `info`) |
193
- | `-h, --help` | Show help |
194
- | `-v, --version` | Show the version |
195
-
196
- A positional `path` argument opens a database file directly, or manages a
197
- folder when it is a directory.
198
-
199
- Every flag has a matching environment variable; **flags override the
200
- environment**:
201
-
202
- | Variable | Default | Description |
203
- | ----------- | ------- | ------------------------------------ |
204
- | `PORT` | `3000` | Port to listen on |
205
- | `HOST` | `0.0.0.0` | Host / interface to bind |
206
- | `DB_PATH` | — | Single SQLite database file |
207
- | `DB_DIR` | — | Folder of `.db`/`.sqlite` files |
208
- | `DB_FILES` | — | Comma-separated explicit database file paths |
209
- | `READONLY` | — | `1` / `true` / `yes` / `on` opens the database(s) read-only |
210
- | `BASE_PATH` | `''` | URL prefix (e.g. `/admin`) |
211
- | `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` |
212
-
213
- Examples:
265
+ ### Disabling Built-in Authentication
266
+
267
+ When deploying behind an external gateway (Cloudflare Zero Trust, OAuth2 Proxy, Authelia):
214
268
 
215
269
  ```bash
216
- admindb # manager UI on http://localhost:3000
217
- admindb -p 8080 # same, on port 8080
218
- admindb ./data/app.db # open a single database file
219
- admindb --open ~/notes.sqlite # open a file directly
220
- admindb -d ./dbs # manage a folder of databases
221
- admindb --files a.db,b.db -r # open two files read-only
270
+ admindb --no-auth
271
+ # or
272
+ ADMINDB_AUTH=false npx admindb
222
273
  ```
223
274
 
224
- ```powershell
225
- $env:DB_DIR='./db'; npm start # PowerShell
226
- # bash/zsh: DB_DIR=./db npm start
227
- ```
275
+ > 📖 **Full Security Guide:** See [**`docs/SECURITY.md`**](docs/SECURITY.md) for the shared security model, filesystem sandboxing, PostgreSQL remote protection, and production deployment checklists.
276
+
277
+
278
+ ---
228
279
 
229
- When the server runs without an explicit single file, the **Databases** landing
230
- page lists the managed databases and includes an **"Open an existing
231
- database"** file browser: navigate folders, pick a database file, and open it.
232
- Opened files are added to the list so you can switch between databases freely.
233
-
234
- **File-browser policy:**
235
-
236
- - When a **folder** is given (`--dir`, a directory path, or `DB_DIR`), browsing
237
- is limited to that folder — it cannot navigate above it and only databases
238
- inside it can be opened.
239
- - When specific **files** are given (`--files`, or `DB_FILES`) without a folder,
240
- file browsing is **disabled** entirely; only the configured databases are
241
- listed.
242
- - With no folder or files, browsing is unrestricted.
243
-
244
- ## Features
245
-
246
- ### 🔍 Browse & edit rows
247
-
248
- - **Table browser** — every table with pagination, sorting (click a column
249
- header), a sticky header, and per-row actions; composite primary keys are
250
- supported (values are URL-encoded and comma-joined in the row endpoints).
251
- - **Type-aware filters** — per-column filter controls: foreign-key dropdowns,
252
- boolean toggles, date and numeric range inputs, plus the exact (`=value`),
253
- comparison (`>5`, `<=10`), prefix (`pre*`) and substring (plain text)
254
- operators on text columns. Filters survive sorting and pagination.
255
- - **Inline (spreadsheet-style) editing** — double-click any cell to edit it in
256
- place with a type-aware control (FK dropdown, boolean toggle, date picker,
257
- number/text input). Changes are staged and highlighted in the grid, then
258
- applied all at once in a single transaction, or discarded.
259
- - **Insert and delete rows** (full CRUD). FK columns become dropdowns; insert
260
- forms pre-fill column defaults from the schema.
261
- - **Bulk row operations** — select rows with checkboxes (or "select all"), then
262
- **delete** them in one transaction (with a warning listing how many rows in
263
- other tables reference them) or **export** only the selected rows as CSV/JSON.
264
- - **Related rows** — every table that has a foreign key pointing at a table gets
265
- its own column at the end of that table's grid; each cell shows how many of its
266
- rows reference that record. Click it to open a nested table with all of the
267
- referencing table's columns and data for that specific record.
268
-
269
- ### ⚡ Query & run SQL
270
-
271
- - **Arbitrary SQL runner** — SELECTs render as a table, `COUNT` queries show a
272
- readable summary, and write statements execute and report affected rows.
273
- - **Saved named queries** — save a query and reload it from a dropdown.
274
- - **"Get query" / preview mode** — generate `CREATE` / `INSERT` / `UPDATE` SQL
275
- from the UI without executing it. The preview modes only return the SQL
276
- string; they never touch the database.
277
- - **SQL dump** — export the whole database as a downloadable `CREATE` + `INSERT`
278
- SQL file.
279
-
280
- ### 🗂️ Design & manage schema
281
-
282
- - **Visual table designer** — create a table with name, type, primary key,
283
- not-null / unique, default value, and foreign-key references, with a live
284
- `CREATE TABLE` SQL preview.
285
- - **Schema editor** — rename the table, add / rename / drop columns, and drop
286
- tables, with relationship-safety checks: drops are refused when the column is
287
- a primary key, has a UNIQUE constraint, is used by an index, is part of a
288
- foreign key, or is referenced by another table's foreign key. Tables
289
- referenced by other tables cannot be dropped. Internal (`_`-prefixed) tables
290
- cannot be renamed or dropped.
291
- - **Indexes** — create indexes (plain or unique, on one or many columns — pick
292
- columns in order, with a live `CREATE INDEX` SQL preview) and drop them from
293
- the schema editor; automatic SQLite (primary-key/unique) indexes are
294
- protected.
295
-
296
- ### 🔄 Import, export & seed data
297
-
298
- - **Export** — table rows or query results as **CSV or JSON** (a whole table or
299
- only selected rows).
300
- - **CSV import** — paste or upload CSV into a table; the header must match
301
- existing columns, and the import runs in a single transaction (a failed row
302
- rolls everything back).
303
- - **Seed data generator** — fill a table with realistic rows in one go. Each
304
- column gets an auto-detected strategy (first/last/full name, email, phone,
305
- city, country, UUID, random integer/decimal/date/datetime/boolean/bytes, a few
306
- words, a sentence, a fixed value, a random value from a list, or "skip — let
307
- the DB default apply"); foreign-key columns can sample real values from the
308
- referenced table. Insert up to 5,000 rows transactionally, or preview the
309
- generated `INSERT` SQL without executing it.
310
-
311
- ### 🚀 Deploy
312
-
313
- - **Read-only mode** — open the database(s) without write access: the file is
314
- opened `SQLITE_OPEN_READONLY` + `query_only`, every write route returns `403`,
315
- and the UI hides/disables all write controls and shows a banner.
316
- - **Standalone CLI / file browser** — `admindb` runs as a full web app; with no
317
- arguments it opens a **Databases** landing page where you can browse the
318
- filesystem and open any SQLite database file, or create new ones. Flags set
319
- the port, open a file, manage a folder, and more (`admindb --help`).
320
- - **Multiple databases** — directory scanning and/or explicit file lists, each
321
- with its own workspace under `/{db}/…`.
322
-
323
- ## Security warning
324
-
325
- > ⚠️ **This tool exposes full, unauthenticated database AND filesystem access.**
326
- > Every page and API route (browse, edit, delete, run arbitrary SQL, change the
327
- > schema, export the whole database, and — in manager mode — browse the
328
- > filesystem to open database files) is available to **anyone who can reach the
329
- > server**.
330
- >
331
- > - **No authentication or authorization is built in.** The routes are **not
332
- > protected**.
333
- > - **It is your responsibility to protect access.** Do **not** expose
334
- > AdminDB to the public internet or to untrusted networks.
335
- > - Recommended ways to protect it:
336
- > - bind the standalone server to `127.0.0.1` (`HOST=127.0.0.1`) and use it
337
- > only from your own machine, and/or
338
- > - run it behind a reverse proxy that requires authentication (Basic auth,
339
- > OAuth, mTLS, …) or inside a VPN / private network.
340
- >
341
- > Treat AdminDB as if it were a remote `sqlite3` shell with write access.
342
-
343
- ## API
344
-
345
- All routes live under `basePath` and return the consistent shape
346
- `{ success, data?, error? }`. In multi-db mode, every route is scoped under the
347
- database, e.g. `/api/app.db/tables`.
280
+ ## 📡 REST API
281
+
282
+ AdminDB exposes a comprehensive REST API under `basePath` returning `{ success, data?, error? }`:
348
283
 
349
284
  ```text
350
- GET /api/tables List tables
351
- GET /api/tables/:table/rows Paginated rows (filters, sorting)
352
- POST /api/tables/:table/rows Insert row
353
- PUT /api/tables/:table/row/:id Update row
354
- DELETE /api/tables/:table/row/:id Delete row
355
- POST /api/query Run arbitrary SQL
356
- POST /api/tables Create table
357
- GET /api/tables/:table/export Download all rows as csv|json
358
- POST /api/tables/:table/seed Generate and insert seed rows
285
+ GET /api/tables List tables
286
+ GET /api/tables/:table/rows Paginated rows (with filtering & sorting)
287
+ GET /api/tables/:table/row/:id Get a single row
288
+ GET /api/tables/:table/row/:id/blob/:column Stream raw BLOB / BYTEA binary data
289
+ GET /api/tables/:table/row/:id/blob/:col/meta BLOB / BYTEA metadata, MIME analysis & hex dump
290
+ PUT /api/tables/:table/row/:id/blob/:column Upload / update binary content
291
+ POST /api/tables/:table/rows Insert row (single or batch)
292
+ PUT /api/tables/:table/row/:id Update row
293
+ DELETE /api/tables/:table/row/:id Delete row
294
+ POST /api/tables/:table/rows/bulk-update Apply staged inline edits atomically
295
+ POST /api/tables/:table/rows/bulk-delete Delete selected rows atomically
296
+ POST /api/tables/:table/seed Generate & insert realistic seed rows
297
+ POST /api/tables Create a new table
298
+ GET /api/tables/:table/schema Inspect table schema & constraints
299
+ GET /api/tables/:table/ddl Get table CREATE SQL & indexes
300
+ POST /api/query Execute arbitrary SQL
301
+ GET /api/databases List managed database connections & files
302
+ ```
303
+
304
+ > 📖 **Full API Reference:** See [**`docs/API.md`**](docs/API.md) for detailed documentation of all 30+ endpoints, query parameters, payload schemas, and TypeScript types.
305
+
306
+ ---
307
+
308
+ ## 📚 Documentation Index
309
+
310
+ | Document | Description |
311
+ | :--- | :--- |
312
+ | [**`docs/EXAMPLES.md`**](docs/EXAMPLES.md) | Comprehensive Environment Variables reference, JSON configs, Express code examples, and deployment recipes. |
313
+ | [**`docs/SECURITY.md`**](docs/SECURITY.md) | Authentication architecture, password hashing, reverse proxy setup, and security checklist. |
314
+ | [**`docs/API.md`**](docs/API.md) | Complete REST API endpoint reference and TypeScript type exports. |
315
+ | [**`CONTRIBUTING.md`**](CONTRIBUTING.md) | Development workflow, running tests, project layout, and contribution guidelines. |
316
+
317
+ ---
318
+
319
+ ## 🤝 Contributing
320
+
321
+ Contributions are welcome! Please check out [**`CONTRIBUTING.md`**](CONTRIBUTING.md) for development setup and testing instructions.
322
+
323
+ ```bash
324
+ git clone https://github.com/MIbnEKhalid/admindb.git
325
+ cd admindb
326
+ npm install
327
+ npm run dev # Live reload development server
328
+ npm test # Run comprehensive unit test suite
359
329
  ```
360
330
 
361
- Plus bulk row operations, CSV import, schema and index management, saved
362
- queries, a data generator — and, in manager mode, database-file management and
363
- the filesystem browser.
364
-
365
- > **Full reference:** every endpoint (including seed config/generate, bulk
366
- > update/delete/export, and the multi-db manager endpoints) is documented in
367
- > **[`docs/API.md`](docs/API.md)**.
368
-
369
- **Behaviour notes:**
370
-
371
- - **Empty input = "not set".** Empty form fields are omitted so DB defaults
372
- apply; `0` is a valid value and is never treated as empty. Insert forms
373
- pre-fill column defaults from the schema (string/number/boolean literals and
374
- `CURRENT_TIMESTAMP`-style defaults).
375
- - **Inline editing is staged, not instant.** Double-click a cell to edit it in
376
- place; edits are buffered locally and highlighted in the grid rather than
377
- written immediately. Press **Apply** to write every pending change in a single
378
- transaction (the page then reloads so related-row counts stay accurate), or
379
- **Discard** to revert. Clearing a text/date/number field stages a `NULL`.
380
- - **Bulk operations** run per page with "select all"; delete first shows an
381
- FK-impact warning (rows may be cascaded away or orphaned depending on the
382
- foreign-key action, and the delete can fail if a constraint blocks it). Both
383
- delete and export are capped at 1000 rows per batch.
384
- - **CSV import** runs in a single transaction — a failed row rolls everything
385
- back.
386
- - **Identifiers are quoted and string values escaped** everywhere SQL is built,
387
- so generated SQL is correct and safe; **"Get query" never executes** — it only
388
- returns the generated SQL string.
389
- - **`COUNT` queries** return a readable summary message instead of a table.
390
- - **Internal table** `_saved_queries` stores saved queries and is kept out of
391
- user-facing FK pickers. Schema initialization is idempotent and safe to run
392
- repeatedly.
393
-
394
- ## Contributing
395
-
396
- PRs are welcome. Development workflow (`npm run dev` for live reload, `npm test`
397
- to build and run the unit suite), project layout, and contribution guidelines
398
- live in **[`CONTRIBUTING.md`](CONTRIBUTING.md)**.
399
-
400
- ## License
401
-
402
- [MIT](./LICENSE) — see the [LICENSE](LICENSE) file.
331
+ ---
332
+
333
+ ## 📄 License
334
+
335
+ [MIT](./LICENSE) © MIbnEKhalid