quar 1.2.6 → 1.3.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.
package/README.md CHANGED
@@ -90,4 +90,6 @@ export default mongoose.model("Post", postSchema);
90
90
  - Modular and extendable
91
91
 
92
92
  ## Contribute
93
- Pull requests, suggestions, and ideas are welcome!
93
+ Pull requests, suggestions, and ideas are welcome!
94
+
95
+ ***Icon: <a href="https://www.flaticon.com/free-icons/thunder" title="thunder icons">Thunder icons created by Hexagon075 - Flaticon</a>***
@@ -0,0 +1,52 @@
1
+ import mongoose from "mongoose";
2
+
3
+ const postSchema = new mongoose.Schema(
4
+ {
5
+ author: {
6
+ type: mongoose.Schema.Types.ObjectId,
7
+ ref: "User",
8
+ required: true,
9
+ },
10
+ title: {
11
+ type: String,
12
+ required: [true, "Post title is required"],
13
+ trim: true,
14
+ maxlength: [120, "Title cannot exceed 120 characters"],
15
+ },
16
+ content: {
17
+ type: String,
18
+ required: [true, "Post content is required"],
19
+ },
20
+ image: {
21
+ type: String,
22
+ default: null,
23
+ },
24
+ likes: [
25
+ {
26
+ type: mongoose.Schema.Types.ObjectId,
27
+ ref: "User",
28
+ },
29
+ ],
30
+ // comments: [
31
+ // {
32
+ // user: {
33
+ // type: mongoose.Schema.Types.ObjectId,
34
+ // ref: "User",
35
+ // },
36
+ // text: {
37
+ // type: String,
38
+ // required: true,
39
+ // },
40
+ // createdAt: {
41
+ // type: Date,
42
+ // default: Date.now,
43
+ // },
44
+ // },
45
+ // ],
46
+ },
47
+ {
48
+ timestamps: true,
49
+ }
50
+ );
51
+
52
+ export default mongoose.model("Post", postSchema);
@@ -0,0 +1,44 @@
1
+ import mongoose from "mongoose";
2
+
3
+ const userSchema = new mongoose.Schema(
4
+ {
5
+ username: {
6
+ type: String,
7
+ required: [true, "Username is required"],
8
+ unique: true,
9
+ trim: true,
10
+ minlength: [3, "Username must be at least 3 characters long"],
11
+ },
12
+ email: {
13
+ type: String,
14
+ required: [true, "Email is required"],
15
+ unique: true,
16
+ lowercase: true,
17
+ match: [/.+@.+\..+/, "Please enter a valid email address"],
18
+ },
19
+ password: {
20
+ type: String,
21
+ required: [true, "Password is required"],
22
+ minlength: [6, "Password must be at least 6 characters long"],
23
+ select: false,
24
+ },
25
+ avatar: {
26
+ type: String,
27
+ },
28
+ bio: {
29
+ type: String,
30
+ maxlength: [160, "Bio cannot exceed 160 characters"],
31
+ },
32
+ posts: [
33
+ {
34
+ type: mongoose.Schema.Types.ObjectId,
35
+ ref: "Post",
36
+ },
37
+ ],
38
+ },
39
+ {
40
+ timestamps: true,
41
+ }
42
+ );
43
+
44
+ export default mongoose.model("User", userSchema);
package/index.js CHANGED
@@ -51,12 +51,12 @@ try {
51
51
  (async () => {
52
52
 
53
53
  await mongoose.connect(`${args.uri || "mongodb://localhost:27017/"}${args.db}`)
54
- console.log( chalk.blue.bold('[INFO]'), chalk.gray("Quar: Database connection established"))
54
+ console.log(chalk.blue.bold('[INFO]'), chalk.gray("Quar: Database connection established"))
55
55
 
56
56
  app.listen(app.get('port'), () => {
57
57
  const url = `http://127.0.0.1:${app.get('port')}`;
58
58
  console.log(chalk.green.bold('[SUCCESS]') + chalk.gray(` Server is running on ${chalk.underline(url)}`));
59
- open(url);
59
+ if (args.open) open(url);
60
60
  });
61
61
  })();
62
62
  } catch (err) {
package/package.json CHANGED
@@ -1,32 +1,32 @@
1
- {
2
- "name": "quar",
3
- "version": "1.2.6",
4
- "description": "This will load all Mongoose models from the folder and start a local web UI to Create, view, update, and delete documents.",
5
- "main": "index.js",
6
- "type": "module",
7
- "bin": {
8
- "quar": "./index.js"
9
- },
10
- "scripts": {
11
- "start": "node index.js",
12
- "dev": "node .\\index.js --model .\\models\\ --db shopDB"
13
- },
14
- "author": "@IsmailBinMujeeb (IsmailBinMujeeb@gmail.com)",
15
- "license": "MIT",
16
- "dependencies": {
17
- "chalk": "^5.4.1",
18
- "express": "^5.1.0",
19
- "minimist": "^1.2.8",
20
- "mongoose": "^8.14.1",
21
- "open": "^10.1.2",
22
- "zare": "^2.1.1"
23
- },
24
- "repository": {
25
- "type": "git",
26
- "url": "https://github.com/IsmailBinMujeeb/quar-studio.git"
27
- },
28
- "bugs": {
29
- "url": "https://github.com/IsmailBinMujeeb/quar-studio/issues"
30
- },
31
- "homepage": "https://github.com/IsmailBinMujeeb/quar-studio#readme"
32
- }
1
+ {
2
+ "name": "quar",
3
+ "version": "1.3.0",
4
+ "description": "This will load all Mongoose models from the folder and start a local web UI to Create, view, update, and delete documents.",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "quar": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js",
12
+ "dev": "node index.js --model ./dummy_models --db quarDB"
13
+ },
14
+ "author": "@IsmailBinMujeeb (IsmailBinMujeeb@gmail.com)",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "chalk": "^5.4.1",
18
+ "express": "^5.1.0",
19
+ "minimist": "^1.2.8",
20
+ "mongoose": "^8.14.1",
21
+ "open": "^10.1.2",
22
+ "zare": "2.6.0"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/IsmailBinMujeeb/quar-studio.git"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/IsmailBinMujeeb/quar-studio/issues"
30
+ },
31
+ "homepage": "https://github.com/IsmailBinMujeeb/quar-studio#readme"
32
+ }
Binary file
@@ -35,7 +35,7 @@ async function activateTab(modelName) {
35
35
 
36
36
  content.dataset.modelName = modelName;
37
37
  document.getElementById("page-value").innerText = 1;
38
- document.querySelectorAll(".operations .btn")?.forEach( btn => btn.disabled = false )
38
+ document.querySelectorAll(".operations .btn")?.forEach(btn => btn.disabled = false)
39
39
  document.querySelector(".operations select").disabled = false;
40
40
 
41
41
  const insertTab = document.querySelector('.insert-tab');
@@ -71,7 +71,7 @@ function closeTab(event, modelName) {
71
71
  function disableAllOptions() {
72
72
  content.dataset.modelName = '';
73
73
 
74
- document.querySelectorAll(".operations .btn")?.forEach( btn => btn.disabled = true );
74
+ document.querySelectorAll(".operations .btn")?.forEach(btn => btn.disabled = true);
75
75
  document.getElementById("document-count").innerText = 0;
76
76
  document.querySelector(".operations select").disabled = true;
77
77
  }
@@ -0,0 +1,34 @@
1
+ .sidebar {
2
+ position: fixed;
3
+ top: 0;
4
+ width: 15vw;
5
+ background: #282b30;
6
+ border-right: 1px solid var(--muted-color);
7
+ height: 100vh;
8
+ }
9
+
10
+ .sidebar-header {
11
+ font-size: 13px;
12
+ font-weight: 600;
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+ background: #1e2124;
17
+ padding: 10px;
18
+ border-bottom: 1px solid var(--muted-color);
19
+ }
20
+
21
+ .sidebar-header .btn {
22
+ color: #E0E0E0;
23
+ background-color: transparent;
24
+ border: none;
25
+ font-size: 12px;
26
+ padding: 0 4px;
27
+ border-radius: 5px;
28
+ cursor: pointer;
29
+ transition: background 0.1s ease-in-out;
30
+
31
+ &:hover {
32
+ background: #2E3A59;
33
+ }
34
+ }
@@ -14,41 +14,6 @@ body {
14
14
  color: #E0E0E0;
15
15
  }
16
16
 
17
- .sidebar {
18
- position: fixed;
19
- top: 0;
20
- width: 15vw;
21
- background: #282b30;
22
- border-right: 1px solid var(--muted-color);
23
- height: 100vh;
24
- }
25
-
26
- .sidebar-header {
27
- font-size: 13px;
28
- font-weight: 600;
29
- display: flex;
30
- align-items: center;
31
- justify-content: space-between;
32
- background: #1e2124;
33
- padding: 10px;
34
- border-bottom: 1px solid var(--muted-color);
35
- }
36
-
37
- .sidebar-header .btn {
38
- color: #E0E0E0;
39
- background-color: transparent;
40
- border: none;
41
- font-size: 12px;
42
- padding: 0 4px;
43
- border-radius: 5px;
44
- cursor: pointer;
45
- transition: background 0.1s ease-in-out;
46
-
47
- &:hover {
48
- background: #2E3A59;
49
- }
50
- }
51
-
52
17
  .model-wrapper {
53
18
  padding: 10px;
54
19
  }
@@ -217,7 +182,7 @@ body {
217
182
  display: none;
218
183
  }
219
184
 
220
- summary::marker{
185
+ summary::marker {
221
186
  color: var(--muted-color);
222
187
  font-size: 10px;
223
188
  margin-right: 10px;
@@ -322,7 +287,7 @@ summary::marker{
322
287
  display: flex;
323
288
  justify-content: left;
324
289
  width: 100%;
325
- padding: 0 10px;
290
+ padding: 0 10px;
326
291
  }
327
292
 
328
293
  .top-container .editDocument {
package/server.js CHANGED
@@ -42,7 +42,7 @@ app.get("/models", async (_, res) => {
42
42
  loadModels(app.locals.modelPath)
43
43
  const modelNames = mongoose.modelNames();
44
44
  modelNames.sort((a, b) => a.localeCompare(b));
45
-
45
+
46
46
  const result = await Promise.all(
47
47
  modelNames.map(async name => {
48
48
  const model = mongoose.model(name);
@@ -50,7 +50,7 @@ app.get("/models", async (_, res) => {
50
50
  return { name, count };
51
51
  })
52
52
  );
53
-
53
+
54
54
  res.status(200).json({ models: result });
55
55
  } catch (error) {
56
56
  res.status(500).json({ error: error.message || "Internal server error" });
@@ -71,6 +71,7 @@ app.get("/models/:modelName", async (req, res) => {
71
71
 
72
72
  res.status(200).json({ documents, schema: modelSchema, totalCount, count, totalPages });
73
73
  } catch (error) {
74
+ console.log(error)
74
75
  res.status(500).json({ error: error.message || "Internal server error" });
75
76
  }
76
77
  });
@@ -138,56 +139,56 @@ app.post("/insert/:modelName", async (req, res) => {
138
139
  try {
139
140
  const { modelName } = req.params;
140
141
  const body = req.body;
141
-
142
+
142
143
  const Model = mongoose.model(modelName);
143
-
144
+
144
145
  if (!Model) return res.status(404).json({ error: "model not found" });
145
-
146
+
146
147
  const paths = Model.schema.paths;
147
-
148
+
148
149
  function getSchema(paths) {
149
-
150
+
150
151
  let schema = {};
151
152
  for (const path in paths) {
152
-
153
+
153
154
  if (path === '_id' || path === '__v') continue;
154
-
155
+
155
156
  if (paths[path].instance === 'Embedded') {
156
157
  schema[path] = getSchema(paths[path].schema.paths);
157
158
  continue;
158
159
  };
159
-
160
+
160
161
  schema[path] = paths[path].instance;
161
162
  }
162
-
163
+
163
164
  return schema;
164
165
  }
165
-
166
+
166
167
  const schema = getSchema(paths)
167
-
168
+
168
169
  function getData(body, schema) {
169
-
170
+
170
171
  let data = {};
171
-
172
+
172
173
  for (const key in schema) {
173
174
  if (typeof schema[key] === 'object' && schema[key] !== null) {
174
-
175
+
175
176
  data[key] = getData(body, schema[key]);
176
177
  continue;
177
178
  }
178
-
179
+
179
180
  if (Object.keys(body).includes(key)) {
180
181
  data[key] = body[key];
181
182
  }
182
183
  }
183
-
184
+
184
185
  return data;
185
186
  }
186
-
187
+
187
188
  const data = getData(body, schema)
188
-
189
+
189
190
  const result = await Model.create(data)
190
-
191
+
191
192
  res.status(200).json(result)
192
193
  } catch (error) {
193
194
  res.status(500).json({ error: error.message || "Internal server error" })
@@ -199,7 +200,7 @@ app.put("/update/:modelName/:id", async (req, res) => {
199
200
  const { modelName, id } = req.params;
200
201
  const model = mongoose.model(modelName);
201
202
  const updatedDoc = req.body;
202
-
203
+
203
204
  delete updatedDoc._id;
204
205
  const result = await model.findByIdAndUpdate(id, updatedDoc, { new: true });
205
206
 
@@ -8,13 +8,13 @@ export default (modelPath) => {
8
8
  if (!fs.existsSync(modelPath)) {
9
9
  console.log(
10
10
  chalk.red.bold('[ERROR]') +
11
- ' The specified model path does not exist:\n ' +
12
- chalk.gray(modelPath)
11
+ ' The specified model path does not exist:\n ' +
12
+ chalk.gray(modelPath)
13
13
  );
14
14
  console.log('Tip: Check the path or create the folder first.');
15
15
  process.exit(1);
16
16
  }
17
-
17
+
18
18
  fs.readdirSync(modelPath).forEach(async (file) => {
19
19
  if (file.endsWith('.js')) {
20
20
  const filePath = path.join(modelPath, file);
package/views/base.zare CHANGED
@@ -1,4 +1,4 @@
1
- import keyboardShortcutsJs "/scripts/keyboardCommands"
1
+ import "/scripts/keyboardCommands"
2
2
 
3
3
  serve (
4
4
  <!DOCTYPE html>
@@ -7,7 +7,7 @@ serve (
7
7
  <meta charset="UTF-8"/>
8
8
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
9
9
  <title>Quar Studio</title>
10
- <link rel="shortcut icon" href="/assets/icon.png" type="image/x-icon"/>
10
+ <link rel="icon" href="/images/icon.png" type="image/x-icon"/>
11
11
  </head>
12
12
  <body>
13
13
  @(slot)
@@ -1,5 +1,5 @@
1
- link insertTabCss "/styles/insertTab"
2
- import insertTabJs "/scripts/insertTab"
1
+ link "/styles/insertTab"
2
+ import "/scripts/insertTab"
3
3
 
4
4
  serve (
5
5
  <div class="insert-tab">
@@ -1,5 +1,5 @@
1
- link popupCss "/styles/popup"
2
- import popupJs "/scripts/popup"
1
+ link "/styles/popup"
2
+ import "/scripts/popup"
3
3
 
4
4
  serve (
5
5
  <div class="popup-overlay" id="popup">
@@ -0,0 +1,12 @@
1
+ link "/styles/sidebar"
2
+
3
+ serve (
4
+ <div class="sidebar">
5
+ <div class="sidebar-header">All Models <button onclick="refreshSideBar()" class="btn">⭮</button></div>
6
+ <div class="model-wrapper">
7
+ @each (models:model) {
8
+ <div class="model" onclick="openModel('@(model.name)')">@(model.name)<span id="@(model.name)-doc-count">@(model.count)</span></div>
9
+ }
10
+ </div>
11
+ </div>
12
+ )
@@ -1,22 +1,15 @@
1
- as Base import "../base.zare"
1
+ as Base import "../base"
2
2
 
3
- as Popup import "../components/popup.zare"
4
- as InsertTab import "../components/insertTab.zare"\
3
+ as Sidebar import "../components/sidebar"
4
+ as Popup import "../components/popup"
5
+ as InsertTab import "../components/insertTab"
5
6
 
6
- link styleCss "/styles/style"
7
- import mainJs "/scripts/main"
7
+ link "/styles/style"
8
+ import "/scripts/main"
8
9
 
9
10
  serve (
10
11
  <Base>
11
- <div class="sidebar">
12
- <div class="sidebar-header">All Models <button onclick="refreshSideBar()" class="btn">⭮</button></div>
13
- <div class="model-wrapper">
14
- @each (models:model) {
15
- <div class="model" onclick="openModel('@(model.name)')">@(model.name)<span id="@(model.name)-doc-count">@(model.count)</span></div>
16
- }
17
- </div>
18
- </div>
19
-
12
+ <Sidebar></Sidebar>
20
13
  <div class="main">
21
14
  <div class="tabs" id="tabs">
22
15
  <div class="tab" id="tab-quar-studio"><img src="/images/icon.png" alt="uv dex icon" width="24px"/></div>
@@ -52,4 +45,4 @@ serve (
52
45
  <Popup/>
53
46
  <InsertTab />
54
47
  </Base>
55
- )
48
+ )