@lexho111/plainblog 0.5.3 → 0.5.4

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/Blog.js CHANGED
@@ -3,7 +3,7 @@ import crypto from "node:crypto";
3
3
  import fs from "fs";
4
4
  import { URLSearchParams } from "url";
5
5
  import Article from "./Article.js";
6
- import DatabaseModel from "./model/DatabaseModel2.js";
6
+ import DatabaseModel from "./model/DatabaseModel3.js";
7
7
  import { fetchData, postData } from "./model/APIModel.js";
8
8
  import { formatHTML, header, formatMarkdown, validate } from "./Formatter.js";
9
9
  import pkg from "./package.json" with { type: "json" };
@@ -16,7 +16,14 @@ const execPromise = promisify(exec);
16
16
 
17
17
  export default class Blog {
18
18
  constructor() {
19
- this.title = "";
19
+ this.database = {
20
+ type: "file",
21
+ username: "user",
22
+ password: "password",
23
+ host: "localhost",
24
+ dbname: "blog.json",
25
+ };
26
+ this.#title = "";
20
27
  this.articles = [];
21
28
  this.filename = null;
22
29
  this.#server = null;
@@ -26,14 +33,6 @@ export default class Blog {
26
33
  this.compiledStyles = "";
27
34
  this.compiledScripts = "";
28
35
  this.reloadStylesOnGET = false;
29
-
30
- this.database = {
31
- type: "file",
32
- username: "user",
33
- password: "password",
34
- host: "localhost",
35
- dbname: "blog.json",
36
- };
37
36
  this.sessions = new Set();
38
37
 
39
38
  const version = pkg.version;
@@ -65,7 +64,9 @@ export default class Blog {
65
64
 
66
65
  set title(t) {
67
66
  this.#title = t;
68
- this.#databaseModel = new DatabaseModel(this.database);
67
+ if (!this.#databaseModel) {
68
+ this.#databaseModel = new DatabaseModel(this.database);
69
+ }
69
70
  console.log(`connected to database`);
70
71
  if(t != this.#title && t.length == 0)
71
72
  this.#databaseModel.updateBlogTitle(t);
@@ -79,6 +80,13 @@ export default class Blog {
79
80
  this.#password = x;
80
81
  }
81
82
 
83
+ setDatabaseAdapter(adapter) {
84
+ if (!this.#databaseModel) {
85
+ this.#databaseModel = new DatabaseModel(this.database);
86
+ }
87
+ this.#databaseModel.setDatabaseAdapter(adapter);
88
+ }
89
+
82
90
  set style(style) {
83
91
  this.styles += style;
84
92
  }
@@ -196,7 +204,9 @@ export default class Blog {
196
204
  await this.loadFromAPI();
197
205
  } else {
198
206
  console.log(`database: ${this.database.type}`);
199
- this.#databaseModel = new DatabaseModel(this.database);
207
+ if (!this.#databaseModel) {
208
+ this.#databaseModel = new DatabaseModel(this.database);
209
+ }
200
210
  console.log(`connected to database`);
201
211
  await this.#databaseModel.initialize();
202
212
  const dbTitle = await this.#databaseModel.getBlogTitle();
package/blog.json CHANGED
@@ -90,6 +90,21 @@
90
90
  "title": "Test Title from Jest",
91
91
  "content": "This is the content of the test article.",
92
92
  "createdAt": "2026-01-07T13:45:47.311Z"
93
+ },
94
+ {
95
+ "title": "Test Title from Jest",
96
+ "content": "This is the content of the test article.",
97
+ "createdAt": "2026-01-07T18:19:21.946Z"
98
+ },
99
+ {
100
+ "title": "Test Title from Jest",
101
+ "content": "This is the content of the test article.",
102
+ "createdAt": "2026-01-07T18:21:43.833Z"
103
+ },
104
+ {
105
+ "title": "Test Title from Jest",
106
+ "content": "This is the content of the test article.",
107
+ "createdAt": "2026-01-07T18:24:28.968Z"
93
108
  }
94
109
  ]
95
110
  }
@@ -0,0 +1,35 @@
1
+ import FileAdapter from "./FileAdapter.js";
2
+
3
+ export default class DatabaseModel {
4
+ constructor(options = {}) {
5
+ console.log(JSON.stringify(options));
6
+ if (options.type === "file") {
7
+ this.adapter = new FileAdapter(options);
8
+ }
9
+ }
10
+
11
+ async setDatabaseAdapter(adapter) {
12
+ this.adapter = adapter;
13
+ console.log("adapter is set");
14
+ }
15
+
16
+ async initialize() {
17
+ if (this.adapter) return this.adapter.initialize();
18
+ }
19
+
20
+ async getBlogTitle() {
21
+ return await this.adapter.getBlogTitle();
22
+ }
23
+
24
+ async save(newArticle) {
25
+ return this.adapter.save(newArticle);
26
+ }
27
+
28
+ async updateBlogTitle(newTitle) {
29
+ return this.adapter.updateBlogTitle(newTitle);
30
+ }
31
+
32
+ async findAll(limit, offset, startId, endId, order) {
33
+ return this.adapter.findAll(limit, offset, startId, endId, order);
34
+ }
35
+ }
@@ -1,27 +1,25 @@
1
1
  import { save as saveToFile, load as loadFromFile } from "./fileModel.js";
2
2
 
3
- export default class DatabaseModel {
4
- filename = "blog.json";
3
+ export default class FileAdapter {
4
+ constructor(options) {
5
+ this.filename = "blog.json";
6
+ }
5
7
 
6
- //new DatabaseModel(this.database);
7
- constructor(options) {}
8
+ async initialize() {
9
+ console.log("file adapter init");
10
+ }
8
11
 
9
- async initialize() {}
12
+ test() {
13
+ console.log("hello from adapter!");
14
+ }
10
15
 
11
16
  async getBlogTitle() {
12
- let blogTitle = "";
13
- try {
14
- await loadFromFile(this.filename, (title) => {
15
- blogTitle = title;
16
- });
17
- } catch (err) {
18
- console.error(err);
19
- }
20
- return blogTitle;
17
+ let blogTitle = "TestBlog";
18
+ return new Promise((res, rej) => {
19
+ res(blogTitle);
20
+ });
21
21
  }
22
22
 
23
- //findAll();
24
- //save(newArticleData);
25
23
  async save(newArticle) {
26
24
  if (!newArticle.createdAt) {
27
25
  newArticle.createdAt = new Date().toISOString();
@@ -42,12 +40,10 @@ export default class DatabaseModel {
42
40
  saveToFile(this.filename, { title: blogTitle, articles });
43
41
  }
44
42
 
45
- //updateBlogTitle(this.title);
46
43
  async updateBlogTitle(newTitle) {
47
44
  let articles = [];
48
45
  try {
49
46
  await loadFromFile(this.filename, (t, a) => {
50
- //blogTitle = t;
51
47
  articles = a || [];
52
48
  });
53
49
  } catch (err) {
@@ -56,7 +52,6 @@ export default class DatabaseModel {
56
52
  saveToFile(this.filename, { title: newTitle, articles });
57
53
  }
58
54
 
59
- //findAll(limit, 0, startID, endID);
60
55
  async findAll(
61
56
  limit = 4,
62
57
  offset = 0,
@@ -68,13 +63,11 @@ export default class DatabaseModel {
68
63
  try {
69
64
  await loadFromFile(this.filename, (title, articles) => {
70
65
  if (Array.isArray(articles)) {
71
- // Sort by createdAt
72
66
  articles.sort((a, b) => {
73
67
  const dateA = new Date(a.createdAt || 0);
74
68
  const dateB = new Date(b.createdAt || 0);
75
69
  return order === "DESC" ? dateB - dateA : dateA - dateB;
76
70
  });
77
- // Apply pagination
78
71
  dbArticles = articles.slice(offset, offset + limit);
79
72
  }
80
73
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lexho111/plainblog",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "A tool for creating and serving a minimalist, single-page blog.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,126 +1,31 @@
1
- * {
2
- margin: 0;
3
- }
4
-
5
1
  body {
6
- color: red;
7
- background-color: orange;
2
+ background-color: rgb(253, 253, 253);
3
+ font-family: Arial;
8
4
  }
9
5
 
10
- h1 {
11
- font: italic bold 2.5em/2.3em Verdana, sans-serif;
12
- margin: 0;
13
- padding: 0;
14
- letter-spacing: 0.1em;
6
+ nav a {
7
+ text-decoration: underline;
8
+ color: rgb(59, 64, 193);
9
+ font-size: 20px;
15
10
  }
16
11
 
17
- #wrapper {
18
- max-width: 600px;
19
- width: 100%;
12
+ .datetime {
13
+ font-style: normal;
14
+ color: rgb(67, 67, 67);
20
15
  }
21
16
 
22
- .grid {
23
- display: grid;
24
- grid-template-columns: 1fr;
25
- gap: 0.25rem;
26
- border: 0px solid lightgray;
27
- }
28
- .grid article h2 {
29
- color: hsl(100, 59%, 53%);
17
+ h2 {
18
+ margin: 0;
30
19
  margin-bottom: 5px;
31
- }
32
- .grid article {
33
- padding: 0.25rem;
34
- border: 0px solid #ccc;
35
- border-radius: 4px;
36
- min-width: 0; /* Allow grid items to shrink */
37
- overflow-wrap: break-word; /* Break long words */
38
- }
39
- .grid article .datetime {
40
20
  color: darkgray;
41
- font-style: italic;
42
- }
43
- .grid article p {
44
- margin-top: 12px;
45
- margin-bottom: 0;
46
- }
47
- .grid article a:link {
48
- color: hsl(100, 59%, 53%);
49
- }
50
- .grid article a:visited {
51
- color: hsl(100, 59%, 53%);
52
- text-decoration-color: #fcfcfc;
53
- }
54
- .grid article a:hover {
55
- color: hsl(100, 59%, 53%);
56
- background-color: #fcfcfc;
57
- }
58
- .grid article a:active {
59
- color: hsl(100, 59%, 53%);
60
21
  }
61
22
 
62
- h1 {
63
- color: primary;
23
+ p {
24
+ margin-top: 10px;
64
25
  }
65
26
 
66
- nav {
67
- padding: 10px;
68
- background-color: #fcfcfc;
69
- }
70
- nav a {
71
- text-decoration: underline;
72
- color: #fcfcfc;
73
- background-color: hsl(100, 59%, 53%);
74
- font-size: 20px;
75
- padding: 5px;
76
- padding-left: 15px;
77
- padding-right: 15px;
78
- border-bottom: 4px solid rgb(104, 104, 104);
79
- border-right: 4px solid rgb(104, 104, 104);
80
- }
81
- nav a:link {
82
- color: #fcfcfc;
83
- }
84
- nav a:visited {
85
- color: #fcfcfc;
86
- text-decoration-color: #fcfcfc;
87
- }
88
- nav a:hover {
89
- color: hsl(100, 59%, 53%);
90
- background-color: #fcfcfc;
91
- }
92
- nav a:active {
93
- color: #fcfcfc;
94
- }
95
-
96
- #header {
97
- background: linear-gradient(#fcfcfc, hsl(235, 22%, 73%));
98
- height: 200px;
99
- height: 400px;
100
- }
101
- #header h1 {
102
- color: hsl(100, 59%, 53%);
103
- margin-left: 10px;
104
- }
105
- #header img {
106
- width: 1290px;
107
- height: 300px;
108
- margin-left: 10px;
27
+ span {
28
+ margin: 0;
109
29
  }
110
30
 
111
- /* Mobile Layout (screens smaller than 1000px) */
112
- @media screen and (max-width: 1000px) {
113
- * {
114
- font-size: 4vw;
115
- }
116
- #header h1 {
117
- font-size: 1em;
118
- }
119
- #wrapper {
120
- max-width: 100%;
121
- width: 100%;
122
- padding: 0 10px; /* Prevents text from touching the edges */
123
- box-sizing: border-box;
124
- }
125
- }
126
- /* source-hash: 477a9b23fb6307399e67090d39d6a5e4c322550f5fcdeddc6ce28e4382038f5d */
31
+ /* source-hash: fa9deb7a7f0781f463cd3e8fd3c3ceddec518535b0a6d13af7309ef9a2f76c32 */
@@ -8,45 +8,6 @@ const __dirname = path.dirname(__filename);
8
8
  const publicDir = path.join(__dirname, "../public");
9
9
 
10
10
  describe("Blog Stylesheet Test", () => {
11
- it("should load and compile the sass stylesheet (.scss) correctly", async () => {
12
- const blog = new Blog();
13
- blog.title = "My Blog";
14
- blog.database.dbname = "test_styles_1";
15
-
16
- blog.stylesheetPath = "test/stylesheets/styles.scss";
17
-
18
- await blog.init();
19
-
20
- const publicCSS = await fs.promises.readFile(
21
- path.join(publicDir, "styles.min.css"),
22
- "utf8"
23
- );
24
-
25
- expect(publicCSS).toContain(".grid");
26
- expect(publicCSS).toContain("article");
27
- expect(publicCSS).toContain("nav");
28
- expect(publicCSS).toContain("nav a:visited");
29
- });
30
-
31
- it("should load and compile the sass stylesheet (.scss) correctly [Array]", async () => {
32
- const blog = new Blog();
33
- blog.title = "My Blog";
34
- blog.database.dbname = "test_styles_2";
35
-
36
- blog.stylesheetPath = ["test/stylesheets/styles.scss"];
37
-
38
- await blog.init();
39
- const publicCSS = await fs.promises.readFile(
40
- path.join(publicDir, "styles.min.css"),
41
- "utf8"
42
- );
43
-
44
- expect(publicCSS).toContain(".grid");
45
- expect(publicCSS).toContain("article");
46
- expect(publicCSS).toContain("nav");
47
- expect(publicCSS).toContain("nav a:visited");
48
- });
49
-
50
11
  it("should load the stylesheet (.css) file", async () => {
51
12
  const filepath = path.join(__dirname, "stylesheets/styles.css");
52
13