@seip/blue-bird 0.7.6 → 0.8.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 (41) hide show
  1. package/.env_example +34 -34
  2. package/AGENTS.md +174 -249
  3. package/LICENSE +21 -21
  4. package/README.md +312 -367
  5. package/{index.js → backend/index.js} +22 -30
  6. package/backend/routes/api.js +57 -57
  7. package/core/app.js +338 -402
  8. package/core/auth.js +262 -256
  9. package/core/cache.js +174 -174
  10. package/core/cli/docker.js +488 -457
  11. package/core/cli/init.js +332 -336
  12. package/core/cli/route.js +42 -42
  13. package/core/config.js +52 -52
  14. package/core/database.js +263 -263
  15. package/core/debug.js +248 -248
  16. package/core/logger.js +115 -115
  17. package/core/middleware.js +27 -27
  18. package/core/router.js +144 -144
  19. package/core/swagger.js +40 -40
  20. package/core/upload.js +77 -77
  21. package/core/validate.js +380 -380
  22. package/docker/Dockerfile +16 -16
  23. package/docker/docker-compose.dev.yml +6 -0
  24. package/docker/docker-compose.mysql.yml +92 -92
  25. package/docker/docker-compose.none.yml +68 -68
  26. package/docker/docker-compose.postgres.yml +93 -93
  27. package/docker/nginx.conf +98 -106
  28. package/docker-compose.yml +92 -92
  29. package/frontend/about.html +98 -0
  30. package/frontend/css/app.css +0 -0
  31. package/frontend/favicon.ico +0 -0
  32. package/frontend/index.html +141 -0
  33. package/frontend/js/bundle.js +8 -0
  34. package/package.json +64 -71
  35. package/frontend/astro.config.mjs +0 -35
  36. package/frontend/public/css/app.css +0 -319
  37. package/frontend/public/favicon.ico +0 -0
  38. package/frontend/src/http/api.js +0 -29
  39. package/frontend/src/layouts/Layout.astro +0 -20
  40. package/frontend/src/pages/about.astro +0 -54
  41. package/frontend/src/pages/index.astro +0 -110
package/core/logger.js CHANGED
@@ -1,115 +1,115 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import Config from "./config.js";
4
- import { getRedisClient } from "./cache.js";
5
-
6
- const __dirname = Config.dirname();
7
-
8
- /**
9
- * Logger class for managing application logs by creating dated folders and log files.
10
- */
11
- class Logger {
12
- /**
13
- * Initializes the Logger instance and ensures the logs directory exists.
14
- */
15
- constructor() {
16
- this.folder = path.join(__dirname, "backend", "logs");
17
- this._currentDay = null;
18
- this._currentDayFolder = null;
19
- if (!fs.existsSync(this.folder)) {
20
- fs.mkdirSync(this.folder, { recursive: true });
21
- }
22
- }
23
-
24
- /**
25
- * Ensures and returns the path to the log folder for the current day.
26
- * Caches the folder path for the current day to avoid repeated fs checks.
27
- * @returns {string} The absolute path to the current day's log folder.
28
- */
29
- nowFolder() {
30
- const today = this.now();
31
-
32
- if (this._currentDay === today && this._currentDayFolder) {
33
- return this._currentDayFolder;
34
- }
35
-
36
- const folder = path.join(this.folder, today);
37
-
38
- if (!fs.existsSync(folder)) {
39
- fs.mkdirSync(folder, { recursive: true });
40
- }
41
-
42
- this._currentDay = today;
43
- this._currentDayFolder = folder;
44
- return folder;
45
- }
46
-
47
- /**
48
- * Gets the current date formatted as YYYY-MM-DD.
49
- * @returns {string} The formatted date string.
50
- */
51
- now() {
52
- return new Date().toISOString().split("T")[0];
53
- }
54
-
55
- /**
56
- * Logs a message to the specified log file or Redis list.
57
- * @private
58
- * @param {string} file - The file name to log to.
59
- * @param {string} level - The log level (e.g. info, error, warn, debug).
60
- * @param {string} message - The log message.
61
- */
62
- async _log(file, level, message) {
63
- const redisClient = getRedisClient();
64
- if (redisClient) {
65
- try {
66
- await redisClient.lPush(`bluebird:logs:${level}`, message);
67
- return;
68
- } catch (err) {
69
- console.error(
70
- `[LOGGER ERROR] Failed to write to Redis logs (${level}):`,
71
- err.message,
72
- );
73
- }
74
- }
75
-
76
- const logFile = path.join(this.nowFolder(), file);
77
- fs.appendFile(logFile, `${message}\n`, (err) => {
78
- if (err) console.error("Logger write error:", err.message);
79
- });
80
- }
81
-
82
- /**
83
- * Appends an informational message.
84
- * @param {string} message - The message to log.
85
- */
86
- info(message) {
87
- this._log("info.log", "info", message);
88
- }
89
-
90
- /**
91
- * Appends an error message.
92
- * @param {string} message - The error message to log.
93
- */
94
- error(message) {
95
- this._log("error.log", "error", message);
96
- }
97
-
98
- /**
99
- * Appends a warning message.
100
- * @param {string} message - The warning message to log.
101
- */
102
- warning(message) {
103
- this._log("warn.log", "warn", message);
104
- }
105
-
106
- /**
107
- * Appends a debug message.
108
- * @param {string} message - The debug message to log.
109
- */
110
- debug(message) {
111
- this._log("debug.log", "debug", message);
112
- }
113
- }
114
-
115
- export default Logger;
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import Config from "./config.js";
4
+ import { getRedisClient } from "./cache.js";
5
+
6
+ const __dirname = Config.dirname();
7
+
8
+ /**
9
+ * Logger class for managing application logs by creating dated folders and log files.
10
+ */
11
+ class Logger {
12
+ /**
13
+ * Initializes the Logger instance and ensures the logs directory exists.
14
+ */
15
+ constructor() {
16
+ this.folder = path.join(__dirname, "backend", "logs");
17
+ this._currentDay = null;
18
+ this._currentDayFolder = null;
19
+ if (!fs.existsSync(this.folder)) {
20
+ fs.mkdirSync(this.folder, { recursive: true });
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Ensures and returns the path to the log folder for the current day.
26
+ * Caches the folder path for the current day to avoid repeated fs checks.
27
+ * @returns {string} The absolute path to the current day's log folder.
28
+ */
29
+ nowFolder() {
30
+ const today = this.now();
31
+
32
+ if (this._currentDay === today && this._currentDayFolder) {
33
+ return this._currentDayFolder;
34
+ }
35
+
36
+ const folder = path.join(this.folder, today);
37
+
38
+ if (!fs.existsSync(folder)) {
39
+ fs.mkdirSync(folder, { recursive: true });
40
+ }
41
+
42
+ this._currentDay = today;
43
+ this._currentDayFolder = folder;
44
+ return folder;
45
+ }
46
+
47
+ /**
48
+ * Gets the current date formatted as YYYY-MM-DD.
49
+ * @returns {string} The formatted date string.
50
+ */
51
+ now() {
52
+ return new Date().toISOString().split("T")[0];
53
+ }
54
+
55
+ /**
56
+ * Logs a message to the specified log file or Redis list.
57
+ * @private
58
+ * @param {string} file - The file name to log to.
59
+ * @param {string} level - The log level (e.g. info, error, warn, debug).
60
+ * @param {string} message - The log message.
61
+ */
62
+ async _log(file, level, message) {
63
+ const redisClient = getRedisClient();
64
+ if (redisClient) {
65
+ try {
66
+ await redisClient.lPush(`bluebird:logs:${level}`, message);
67
+ return;
68
+ } catch (err) {
69
+ console.error(
70
+ `[LOGGER ERROR] Failed to write to Redis logs (${level}):`,
71
+ err.message,
72
+ );
73
+ }
74
+ }
75
+
76
+ const logFile = path.join(this.nowFolder(), file);
77
+ fs.appendFile(logFile, `${message}\n`, (err) => {
78
+ if (err) console.error("Logger write error:", err.message);
79
+ });
80
+ }
81
+
82
+ /**
83
+ * Appends an informational message.
84
+ * @param {string} message - The message to log.
85
+ */
86
+ info(message) {
87
+ this._log("info.log", "info", message);
88
+ }
89
+
90
+ /**
91
+ * Appends an error message.
92
+ * @param {string} message - The error message to log.
93
+ */
94
+ error(message) {
95
+ this._log("error.log", "error", message);
96
+ }
97
+
98
+ /**
99
+ * Appends a warning message.
100
+ * @param {string} message - The warning message to log.
101
+ */
102
+ warning(message) {
103
+ this._log("warn.log", "warn", message);
104
+ }
105
+
106
+ /**
107
+ * Appends a debug message.
108
+ * @param {string} message - The debug message to log.
109
+ */
110
+ debug(message) {
111
+ this._log("debug.log", "debug", message);
112
+ }
113
+ }
114
+
115
+ export default Logger;
@@ -1,27 +1,27 @@
1
- import Auth from "./auth.js";
2
-
3
- /**
4
- * Common middlewares for the Blue Bird framework.
5
- */
6
- const Middleware = {
7
- /**
8
- * Authentication protection middleware.
9
- * @type {Function}
10
- */
11
- auth: Auth.protect(),
12
-
13
- /**
14
- * Web authentication protection middleware (redirects to home if fails).
15
- * @type {Function}
16
- */
17
- webAuth: Auth.protect({ redirect: "/" }),
18
-
19
- /**
20
- * Logging middleware (can be extended).
21
- */
22
- logger: (req, res, next) => {
23
- next();
24
- }
25
- };
26
-
27
- export default Middleware;
1
+ import Auth from "./auth.js";
2
+
3
+ /**
4
+ * Common middlewares for the Blue Bird framework.
5
+ */
6
+ const Middleware = {
7
+ /**
8
+ * Authentication protection middleware.
9
+ * @type {Function}
10
+ */
11
+ auth: Auth.protect(),
12
+
13
+ /**
14
+ * Web authentication protection middleware (redirects to home if fails).
15
+ * @type {Function}
16
+ */
17
+ webAuth: Auth.protect({ redirect: "/" }),
18
+
19
+ /**
20
+ * Logging middleware (can be extended).
21
+ */
22
+ logger: (req, res, next) => {
23
+ next();
24
+ }
25
+ };
26
+
27
+ export default Middleware;
package/core/router.js CHANGED
@@ -1,144 +1,144 @@
1
- import express from "express";
2
- import Config from "./config.js";
3
-
4
-
5
- const props = Config.props();
6
-
7
- /**
8
- * Router wrapper class for handling Express routing logic.
9
- * When created with { seo: true }, all GET routes registered on this router
10
- * are automatically included in the generated sitemap.xml and robots.txt.
11
- */
12
- class Router {
13
- /**
14
- * Creates a new Router instance.
15
- * @param {string} [path="/"] - The base path for this router.
16
- * @param {Object} [options={}] - Router configuration options.
17
- * @param {boolean} [options.seo=false] - When true, GET routes on this router are included in sitemap/robots.txt.
18
- * @param {string[]} [options.languages=[]] - Language prefixes for SEO route generation (e.g., ["en", "es"]).
19
- * @example
20
- * const router = new Router("/", { seo: true, languages: ["en", "es"] });
21
- * router.get("/", (req, res) => {
22
- * Template.render(res, "index", { metaTags: { titleMeta: "Home" } });
23
- * });
24
- */
25
- constructor(path = "/", options = {}) {
26
- this.router = express.Router();
27
- this.path = path;
28
- this._seo = options.seo ?? false;
29
- this._languages = options.languages || [];
30
- }
31
-
32
- /**
33
- * Registers a middleware on this router.
34
- * @param {...Function} middleware - Middleware functions.
35
- * @example
36
- * router.use(Auth.protect());
37
- * router.use(App.helmet());
38
- */
39
- use(...middleware) {
40
- this.router.use(...middleware);
41
- }
42
-
43
- /**
44
- * Registers a GET route handler.
45
- * If this router was created with { seo: true }, the route path is automatically
46
- * registered for sitemap.xml and robots.txt generation.
47
- * @param {string} path - The relative path for the GET route.
48
- * @param {...Function} callback - One or more handler functions (middlewares and controller).
49
- * @example
50
- * router.get("/about", (req, res) => {
51
- * Template.render(res, "about", {
52
- * metaTags: { titleMeta: "About Us" }
53
- * });
54
- * });
55
- */
56
- get(path, ...callback) {
57
- if (path === "/*" || path === "*") {
58
- path = /.*/;
59
- }
60
-
61
- this.router.get(path, callback);
62
- }
63
-
64
- /**
65
- * Registers a POST route handler.
66
- * @param {string} path - The relative path for the POST route.
67
- * @param {...Function} callback - One or more handler functions (middlewares and controller).
68
- * @example
69
- * router.post("/users", (req, res) => {
70
- * res.json({ message: "User created successfully" })
71
- * })
72
- */
73
- post(path, ...callback) {
74
- if (path === "/*" || path === "*") {
75
- path = /.*/;
76
- }
77
- this.router.post(path, callback);
78
- }
79
-
80
- /**
81
- * Registers a PUT route handler.
82
- * @param {string} path - The relative path for the PUT route.
83
- * @param {...Function} callback - One or more handler functions (middlewares and controller).
84
- * @example
85
- * router.put("/users/:id", (req, res) => {
86
- * res.json({ message: "User updated successfully" })
87
- * })
88
- */
89
- put(path, ...callback) {
90
- this.router.put(path, callback);
91
- }
92
-
93
- /**
94
- * Registers a DELETE route handler.
95
- * @param {string} path - The relative path for the DELETE route.
96
- * @param {...Function} callback - One or more handler functions (middlewares and controller).
97
- * @example
98
- * router.delete("/users/:id", (req, res) => {
99
- * res.json({ message: "User deleted successfully" })
100
- * })
101
- */
102
- delete(path, ...callback) {
103
- this.router.delete(path, callback);
104
- }
105
-
106
- /**
107
- * Registers a PATCH route handler.
108
- * @param {string} path - The relative path for the PATCH route.
109
- * @param {...Function} callback - One or more handler functions (middlewares and controller).
110
- * @example
111
- * router.patch("/users/:id", (req, res) => {
112
- * res.json({ message: "User patched successfully" })
113
- * })
114
- */
115
- patch(path, ...callback) {
116
- this.router.patch(path, callback);
117
- }
118
-
119
- /**
120
- * Registers an OPTIONS route handler.
121
- * @param {string} path - The relative path for the OPTIONS route.
122
- * @param {...Function} callback - One or more handler functions (middlewares and controller).
123
- */
124
- options(path, ...callback) {
125
- this.router.options(path, callback);
126
- }
127
-
128
- /**
129
- * Returns the underlying Express router instance.
130
- * @returns {import('express').Router}
131
- */
132
- getRouter() {
133
- return this.router;
134
- }
135
-
136
- /**
137
- * Returns the base path associated with this router.
138
- * @returns {string}
139
- */
140
- getPath() {
141
- return this.path;
142
- }
143
- }
144
- export default Router;
1
+ import express from "express";
2
+ import Config from "./config.js";
3
+
4
+
5
+ const props = Config.props();
6
+
7
+ /**
8
+ * Router wrapper class for handling Express routing logic.
9
+ * When created with { seo: true }, all GET routes registered on this router
10
+ * are automatically included in the generated sitemap.xml and robots.txt.
11
+ */
12
+ class Router {
13
+ /**
14
+ * Creates a new Router instance.
15
+ * @param {string} [path="/"] - The base path for this router.
16
+ * @param {Object} [options={}] - Router configuration options.
17
+ * @param {boolean} [options.seo=false] - When true, GET routes on this router are included in sitemap/robots.txt.
18
+ * @param {string[]} [options.languages=[]] - Language prefixes for SEO route generation (e.g., ["en", "es"]).
19
+ * @example
20
+ * const router = new Router("/", { seo: true, languages: ["en", "es"] });
21
+ * router.get("/", (req, res) => {
22
+ * Template.render(res, "index", { metaTags: { titleMeta: "Home" } });
23
+ * });
24
+ */
25
+ constructor(path = "/", options = {}) {
26
+ this.router = express.Router();
27
+ this.path = path;
28
+ this._seo = options.seo ?? false;
29
+ this._languages = options.languages || [];
30
+ }
31
+
32
+ /**
33
+ * Registers a middleware on this router.
34
+ * @param {...Function} middleware - Middleware functions.
35
+ * @example
36
+ * router.use(Auth.protect());
37
+ * router.use(App.helmet());
38
+ */
39
+ use(...middleware) {
40
+ this.router.use(...middleware);
41
+ }
42
+
43
+ /**
44
+ * Registers a GET route handler.
45
+ * If this router was created with { seo: true }, the route path is automatically
46
+ * registered for sitemap.xml and robots.txt generation.
47
+ * @param {string} path - The relative path for the GET route.
48
+ * @param {...Function} callback - One or more handler functions (middlewares and controller).
49
+ * @example
50
+ * router.get("/about", (req, res) => {
51
+ * Template.render(res, "about", {
52
+ * metaTags: { titleMeta: "About Us" }
53
+ * });
54
+ * });
55
+ */
56
+ get(path, ...callback) {
57
+ if (path === "/*" || path === "*") {
58
+ path = /.*/;
59
+ }
60
+
61
+ this.router.get(path, callback);
62
+ }
63
+
64
+ /**
65
+ * Registers a POST route handler.
66
+ * @param {string} path - The relative path for the POST route.
67
+ * @param {...Function} callback - One or more handler functions (middlewares and controller).
68
+ * @example
69
+ * router.post("/users", (req, res) => {
70
+ * res.json({ message: "User created successfully" })
71
+ * })
72
+ */
73
+ post(path, ...callback) {
74
+ if (path === "/*" || path === "*") {
75
+ path = /.*/;
76
+ }
77
+ this.router.post(path, callback);
78
+ }
79
+
80
+ /**
81
+ * Registers a PUT route handler.
82
+ * @param {string} path - The relative path for the PUT route.
83
+ * @param {...Function} callback - One or more handler functions (middlewares and controller).
84
+ * @example
85
+ * router.put("/users/:id", (req, res) => {
86
+ * res.json({ message: "User updated successfully" })
87
+ * })
88
+ */
89
+ put(path, ...callback) {
90
+ this.router.put(path, callback);
91
+ }
92
+
93
+ /**
94
+ * Registers a DELETE route handler.
95
+ * @param {string} path - The relative path for the DELETE route.
96
+ * @param {...Function} callback - One or more handler functions (middlewares and controller).
97
+ * @example
98
+ * router.delete("/users/:id", (req, res) => {
99
+ * res.json({ message: "User deleted successfully" })
100
+ * })
101
+ */
102
+ delete(path, ...callback) {
103
+ this.router.delete(path, callback);
104
+ }
105
+
106
+ /**
107
+ * Registers a PATCH route handler.
108
+ * @param {string} path - The relative path for the PATCH route.
109
+ * @param {...Function} callback - One or more handler functions (middlewares and controller).
110
+ * @example
111
+ * router.patch("/users/:id", (req, res) => {
112
+ * res.json({ message: "User patched successfully" })
113
+ * })
114
+ */
115
+ patch(path, ...callback) {
116
+ this.router.patch(path, callback);
117
+ }
118
+
119
+ /**
120
+ * Registers an OPTIONS route handler.
121
+ * @param {string} path - The relative path for the OPTIONS route.
122
+ * @param {...Function} callback - One or more handler functions (middlewares and controller).
123
+ */
124
+ options(path, ...callback) {
125
+ this.router.options(path, callback);
126
+ }
127
+
128
+ /**
129
+ * Returns the underlying Express router instance.
130
+ * @returns {import('express').Router}
131
+ */
132
+ getRouter() {
133
+ return this.router;
134
+ }
135
+
136
+ /**
137
+ * Returns the base path associated with this router.
138
+ * @returns {string}
139
+ */
140
+ getPath() {
141
+ return this.path;
142
+ }
143
+ }
144
+ export default Router;
package/core/swagger.js CHANGED
@@ -1,40 +1,40 @@
1
- import { execSync } from "node:child_process";
2
-
3
- class SwaggerCli {
4
- install() {
5
- const dependencies = this.checkDependencies();
6
- if (dependencies.missingDependencies.length > 0) {
7
- console.log("Installing dependencies...");
8
- console.log(`Installing swagger-jsdoc...`);
9
- execSync(`npm install swagger-jsdoc@6.2.8`, { stdio: "inherit" });
10
- console.log(`Installing swagger-ui-express...`);
11
- execSync(`npm install swagger-ui-express@5.0.1`, { stdio: "inherit" });
12
- }
13
- }
14
- checkDependencies() {
15
- const dependencies = [
16
- "swagger-jsdoc",
17
- "swagger-ui-express"
18
- ];
19
- const missingDependencies = [];
20
- dependencies.forEach(dependency => {
21
- if (!this.checkDependency(dependency)) {
22
- missingDependencies.push(dependency);
23
- }
24
- });
25
- return {
26
- missingDependencies
27
- };
28
- }
29
- checkDependency(dependency) {
30
- try {
31
- require.resolve(dependency);
32
- return true;
33
- } catch (error) {
34
- return false;
35
- }
36
- }
37
- }
38
-
39
- const swaggerExecutor = new SwaggerCli();
40
- swaggerExecutor.install();
1
+ import { execSync } from "node:child_process";
2
+
3
+ class SwaggerCli {
4
+ install() {
5
+ const dependencies = this.checkDependencies();
6
+ if (dependencies.missingDependencies.length > 0) {
7
+ console.log("Installing dependencies...");
8
+ console.log(`Installing swagger-jsdoc...`);
9
+ execSync(`npm install swagger-jsdoc@6.2.8`, { stdio: "inherit" });
10
+ console.log(`Installing swagger-ui-express...`);
11
+ execSync(`npm install swagger-ui-express@5.0.1`, { stdio: "inherit" });
12
+ }
13
+ }
14
+ checkDependencies() {
15
+ const dependencies = [
16
+ "swagger-jsdoc",
17
+ "swagger-ui-express"
18
+ ];
19
+ const missingDependencies = [];
20
+ dependencies.forEach(dependency => {
21
+ if (!this.checkDependency(dependency)) {
22
+ missingDependencies.push(dependency);
23
+ }
24
+ });
25
+ return {
26
+ missingDependencies
27
+ };
28
+ }
29
+ checkDependency(dependency) {
30
+ try {
31
+ require.resolve(dependency);
32
+ return true;
33
+ } catch (error) {
34
+ return false;
35
+ }
36
+ }
37
+ }
38
+
39
+ const swaggerExecutor = new SwaggerCli();
40
+ swaggerExecutor.install();