@seip/blue-bird 1.1.3 → 1.1.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/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +10 -0
- package/AGENTS.md +41 -4
- package/README.md +55 -1
- package/core/cli/docker.js +10 -1
- package/core/cli/doctor.js +294 -0
- package/core/cli/init.js +34 -5
- package/core/cli/migrate.js +342 -0
- package/core/cli/nginx.js +138 -0
- package/core/cli/route.js +144 -43
- package/core/cli/swagger.js +2 -0
- package/core/index.d.ts +7 -0
- package/core/queue.js +121 -0
- package/core/validate.js +3 -2
- package/docker/nginx.conf +23 -0
- package/frontend/index.html +144 -144
- package/frontend/js/bluebird.d.ts +423 -0
- package/frontend/js/bluebird.js +1121 -716
- package/jsconfig.json +15 -0
- package/package.json +7 -2
package/core/queue.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getRedisClient } from "./cache.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lightweight background queue worker module with Redis and in-memory fallback.
|
|
7
|
+
*/
|
|
8
|
+
class QueueManager {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.handlers = new Map();
|
|
11
|
+
this.memoryQueue = [];
|
|
12
|
+
this.isProcessing = false;
|
|
13
|
+
this.redisPrefix = "bluebird:queue:";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Registers a job handler function.
|
|
18
|
+
* @param {string} jobName - Name of the job.
|
|
19
|
+
* @param {Function} handler - Async function(payload).
|
|
20
|
+
*/
|
|
21
|
+
process(jobName, handler) {
|
|
22
|
+
if (typeof handler !== "function") {
|
|
23
|
+
throw new Error(`Handler for job '${jobName}' must be a function.`);
|
|
24
|
+
}
|
|
25
|
+
this.handlers.set(jobName, handler);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Dispatches a new job to the queue.
|
|
30
|
+
* @param {string} jobName - Name of the job.
|
|
31
|
+
* @param {any} payload - Data payload to pass to the handler.
|
|
32
|
+
* @param {object} [options] - Options (e.g. delayMs).
|
|
33
|
+
* @returns {Promise<boolean>}
|
|
34
|
+
*/
|
|
35
|
+
async dispatch(jobName, payload = {}, options = {}) {
|
|
36
|
+
const jobItem = {
|
|
37
|
+
id: `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
38
|
+
name: jobName,
|
|
39
|
+
payload,
|
|
40
|
+
createdAt: new Date().toISOString(),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const redis = getRedisClient();
|
|
44
|
+
if (redis && redis.isReady) {
|
|
45
|
+
try {
|
|
46
|
+
await redis.lPush(`${this.redisPrefix}jobs`, JSON.stringify(jobItem));
|
|
47
|
+
return true;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error("[QUEUE ERROR] Failed to dispatch job to Redis:", err.message);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// In-memory fallback
|
|
54
|
+
if (options.delayMs && options.delayMs > 0) {
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
this.memoryQueue.push(jobItem);
|
|
57
|
+
this.runMemoryWorker();
|
|
58
|
+
}, options.delayMs);
|
|
59
|
+
} else {
|
|
60
|
+
this.memoryQueue.push(jobItem);
|
|
61
|
+
setImmediate(() => this.runMemoryWorker());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Executes in-memory queue jobs sequentially.
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
async runMemoryWorker() {
|
|
72
|
+
if (this.isProcessing || this.memoryQueue.length === 0) return;
|
|
73
|
+
this.isProcessing = true;
|
|
74
|
+
|
|
75
|
+
while (this.memoryQueue.length > 0) {
|
|
76
|
+
const job = this.memoryQueue.shift();
|
|
77
|
+
if (!job) continue;
|
|
78
|
+
|
|
79
|
+
const handler = this.handlers.get(job.name);
|
|
80
|
+
if (!handler) {
|
|
81
|
+
console.warn(`[QUEUE WARN] No handler registered for job '${job.name}'.`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await handler(job.payload);
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error(`[QUEUE ERROR] Error processing job '${job.name}' (${job.id}):`, err);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.isProcessing = false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Auto-loads all job definition files from backend/jobs/.
|
|
97
|
+
*/
|
|
98
|
+
async loadJobs(jobsDir = path.resolve(process.cwd(), "backend/jobs")) {
|
|
99
|
+
if (!fs.existsSync(jobsDir)) return;
|
|
100
|
+
|
|
101
|
+
const files = fs
|
|
102
|
+
.readdirSync(jobsDir)
|
|
103
|
+
.filter((f) => f.endsWith(".js") || f.endsWith(".mjs"));
|
|
104
|
+
|
|
105
|
+
for (const file of files) {
|
|
106
|
+
const fullPath = path.join(jobsDir, file);
|
|
107
|
+
try {
|
|
108
|
+
const module = await import(`file://${fullPath}`);
|
|
109
|
+
if (typeof module.default === "function") {
|
|
110
|
+
const jobName = path.basename(file, path.extname(file));
|
|
111
|
+
this.process(jobName, module.default);
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error(`[QUEUE ERROR] Failed to load job file '${file}':`, err.message);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const Queue = new QueueManager();
|
|
121
|
+
export default Queue;
|
package/core/validate.js
CHANGED
|
@@ -148,19 +148,20 @@ class Validator {
|
|
|
148
148
|
* const result = await loginValidator.validate(req);
|
|
149
149
|
*/
|
|
150
150
|
async validate(req) {
|
|
151
|
+
const isExpressReq = req && (req.body !== undefined || req.headers !== undefined);
|
|
151
152
|
let lang =
|
|
152
153
|
req?.body?.lang ||
|
|
153
154
|
req?.query?.lang ||
|
|
154
155
|
req?.params?.lang ||
|
|
155
156
|
req?.cookies?.lang ||
|
|
156
|
-
req?.headers["accept-language"]?.split(",")[0]?.split("-")[0] ||
|
|
157
|
+
req?.headers?.["accept-language"]?.split(",")[0]?.split("-")[0] ||
|
|
157
158
|
req?.session?.lang ||
|
|
158
159
|
this.lang_default ||
|
|
159
160
|
"es";
|
|
160
161
|
const msg = this.messages[lang] || this.messages.es;
|
|
161
162
|
const errors = [];
|
|
162
163
|
const messages = [];
|
|
163
|
-
const body = req.body || {};
|
|
164
|
+
const body = isExpressReq ? (req.body || {}) : (req || {});
|
|
164
165
|
|
|
165
166
|
for (const [field, config] of Object.entries(this.schema)) {
|
|
166
167
|
let value = body[field];
|
package/docker/nginx.conf
CHANGED
|
@@ -65,6 +65,29 @@ http {
|
|
|
65
65
|
try_files $uri =404;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
# -------------------------------------------------------------
|
|
69
|
+
# Optional: Protected Static Frontend Pages (auth_request)
|
|
70
|
+
# Uncomment this block to protect private static pages (e.g. /dashboard)
|
|
71
|
+
# Nginx will subrequest Express /api/auth/check before serving HTML.
|
|
72
|
+
# -------------------------------------------------------------
|
|
73
|
+
# location ~ ^/(dashboard|admin|app|account) {
|
|
74
|
+
# auth_request /api/auth/check;
|
|
75
|
+
# error_page 401 = @login_redirect;
|
|
76
|
+
# try_files $uri $uri.html /index.html =404;
|
|
77
|
+
# }
|
|
78
|
+
#
|
|
79
|
+
# location = /api/auth/check {
|
|
80
|
+
# internal;
|
|
81
|
+
# proxy_pass http://app:3000/api/auth/check;
|
|
82
|
+
# proxy_pass_request_body off;
|
|
83
|
+
# proxy_set_header Content-Length "";
|
|
84
|
+
# proxy_set_header X-Original-URI $request_uri;
|
|
85
|
+
# }
|
|
86
|
+
#
|
|
87
|
+
# location @login_redirect {
|
|
88
|
+
# return 302 /login?redirect=$request_uri;
|
|
89
|
+
# }
|
|
90
|
+
|
|
68
91
|
location / {
|
|
69
92
|
add_header Cache-Control "no-cache";
|
|
70
93
|
try_files $uri $uri.html $uri/ @node_app;
|
package/frontend/index.html
CHANGED
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" data-theme="dark">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>Blue Bird Framework</title>
|
|
8
|
-
<link rel="canonical" href="https://seip25.github.io/Blue-bird/" />
|
|
9
|
-
<meta name="description"
|
|
10
|
-
content="Blue Bird is a fast, structured, and opinionated Express.js framework for developers who want visual elegance and performance with raw HTML and CSS.">
|
|
11
|
-
<meta name="keywords" content="Blue Bird, Express.js, Node.js, Semantic CSS, Performance, Nginx, Redis">
|
|
12
|
-
<meta name="author" content="Seip25">
|
|
13
|
-
<link rel="icon" href="/images/favicon.ico" />
|
|
14
|
-
<link rel="stylesheet" href="/css/bluebird.css" />
|
|
15
|
-
<script src="/js/bluebird.js"></script>
|
|
16
|
-
</head>
|
|
17
|
-
|
|
18
|
-
<body>
|
|
19
|
-
<header>
|
|
20
|
-
<nav>
|
|
21
|
-
<a href="/" class="flex items-center gap-2">
|
|
22
|
-
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
23
|
-
stroke-linecap="round" stroke-linejoin="round" class="text-blue">
|
|
24
|
-
<path d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
|
25
|
-
</svg>
|
|
26
|
-
<strong class="text-xl text-gradient">Blue Bird</strong>
|
|
27
|
-
</a>
|
|
28
|
-
</nav>
|
|
29
|
-
</header>
|
|
30
|
-
|
|
31
|
-
<main>
|
|
32
|
-
<aside>
|
|
33
|
-
<h4>Menu</h4>
|
|
34
|
-
<a href="/" class="active badge badge-glow px-3 py-3">Home</a>
|
|
35
|
-
<a href="/about">About</a>
|
|
36
|
-
<a href="https://seip25.github.io/Blue-bird-css/" target="_blank">Blue Bird CSS</a>
|
|
37
|
-
<a href="https://github.com/seip25/Blue-bird" target="_blank" role="button"
|
|
38
|
-
class="btn-sm outline">GitHub</a>
|
|
39
|
-
</aside>
|
|
40
|
-
|
|
41
|
-
<div>
|
|
42
|
-
<section class="hero ">
|
|
43
|
-
<h1 class="mt-3 text-gradient font-bold">Hello, Developer!</h1>
|
|
44
|
-
<p class="lead">
|
|
45
|
-
Blue Bird is a fast, structured, and opinionated Express.js framework for developers who want
|
|
46
|
-
visual elegance and performance with raw HTML and CSS.
|
|
47
|
-
</p>
|
|
48
|
-
<div class="hero-actions">
|
|
49
|
-
<a href="/about" role="button">About Us →</a>
|
|
50
|
-
<a href="https://seip25.github.io/Blue-bird-css/" target="_blank" role="button" class="outline">Blue
|
|
51
|
-
Bird CSS Docs</a>
|
|
52
|
-
<a href="https://github.com/seip25/Blue-bird" target="_blank" role="button"
|
|
53
|
-
class="outline">GitHub</a>
|
|
54
|
-
</div>
|
|
55
|
-
</section>
|
|
56
|
-
|
|
57
|
-
<div class="mt-8">
|
|
58
|
-
<div class="grid cols-3 gap-4">
|
|
59
|
-
<article class="hover-glow ">
|
|
60
|
-
<header>
|
|
61
|
-
<span class="badge badge-primary mb-2">Rendering</span>
|
|
62
|
-
<h3 class="text-gradient-blue">Ultra-Fast HTML Render</h3>
|
|
63
|
-
</header>
|
|
64
|
-
<p>Direct static HTML rendering using high-speed string placeholder
|
|
65
|
-
replacement, bypassing heavy
|
|
66
|
-
parsing engines completely.</p>
|
|
67
|
-
</article>
|
|
68
|
-
|
|
69
|
-
<article class="hover-glow ">
|
|
70
|
-
<header>
|
|
71
|
-
<span class="badge badge-primary mb-2">Performance</span>
|
|
72
|
-
<h3 class="text-gradient-blue">In-Memory Caching</h3>
|
|
73
|
-
</header>
|
|
74
|
-
<p>Configurable time-to-live caching. Rendered pages are stored in RAM for
|
|
75
|
-
instant,
|
|
76
|
-
sub-millisecond
|
|
77
|
-
delivery in production.</p>
|
|
78
|
-
</article>
|
|
79
|
-
|
|
80
|
-
<article class="hover-glow ">
|
|
81
|
-
<header>
|
|
82
|
-
<span class="badge badge-primary mb-2">Security</span>
|
|
83
|
-
<h3 class="text-gradient-blue">JWT & Route Security</h3>
|
|
84
|
-
</header>
|
|
85
|
-
<p>Secure cookie management, GCM-encrypted JWT tokens, custom helmet
|
|
86
|
-
configurations, and route
|
|
87
|
-
rate
|
|
88
|
-
limiting built-in.</p>
|
|
89
|
-
</article>
|
|
90
|
-
|
|
91
|
-
<article class="hover-glow ">
|
|
92
|
-
<header>
|
|
93
|
-
<span class="badge badge-primary mb-2">Database</span>
|
|
94
|
-
<h3 class="text-gradient-blue">Unified DB Layer</h3>
|
|
95
|
-
</header>
|
|
96
|
-
<p>Built-in support for SQLite (WAL mode), MySQL, and PostgreSQL with
|
|
97
|
-
connection pooling,
|
|
98
|
-
retries,
|
|
99
|
-
and Redis query caching.</p>
|
|
100
|
-
</article>
|
|
101
|
-
|
|
102
|
-
<article class="hover-glow ">
|
|
103
|
-
<header>
|
|
104
|
-
<span class="badge badge-primary mb-2">DevOps</span>
|
|
105
|
-
<h3 class="text-gradient-blue">Zero-Config Docker</h3>
|
|
106
|
-
</header>
|
|
107
|
-
<p>Containerized development & production orchestration with Nginx
|
|
108
|
-
static delivery and Redis
|
|
109
|
-
caching out of the box.</p>
|
|
110
|
-
</article>
|
|
111
|
-
|
|
112
|
-
<article class="hover-glow ">
|
|
113
|
-
<header>
|
|
114
|
-
<span class="badge badge-primary mb-2">Design</span>
|
|
115
|
-
<h3 class="text-gradient-blue">Pure Semantic CSS</h3>
|
|
116
|
-
</header>
|
|
117
|
-
<p>Clean semantic markup styled by <a href="https://seip25.github.io/Blue-bird-css/"
|
|
118
|
-
target="_blank">Blue Bird CSS</a> with
|
|
119
|
-
zero JavaScript framework bloat or compile steps.
|
|
120
|
-
</p>
|
|
121
|
-
</article>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</main>
|
|
126
|
-
|
|
127
|
-
<footer>
|
|
128
|
-
<div class="footer-content">
|
|
129
|
-
<p>Powered by <strong class="text-gradient">Blue Bird Framework</strong> &
|
|
130
|
-
<strong class="text-gradient-blue">
|
|
131
|
-
<a href="https://seip25.github.io/Blue-bird-css/" target="_blank">Blue Bird
|
|
132
|
-
CSS</a>
|
|
133
|
-
</strong>.
|
|
134
|
-
</p>
|
|
135
|
-
<div class="lang-switcher">
|
|
136
|
-
<a href="/" class="lang-link">Home</a>
|
|
137
|
-
<a href="/about" class="lang-link">About</a>
|
|
138
|
-
<a href="https://github.com/seip25/Blue-bird" target="_blank" class="github-star mt-2">★
|
|
139
|
-
GitHub</a>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
</footer>
|
|
143
|
-
</body>
|
|
144
|
-
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="dark">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Blue Bird Framework</title>
|
|
8
|
+
<link rel="canonical" href="https://seip25.github.io/Blue-bird/" />
|
|
9
|
+
<meta name="description"
|
|
10
|
+
content="Blue Bird is a fast, structured, and opinionated Express.js framework for developers who want visual elegance and performance with raw HTML and CSS.">
|
|
11
|
+
<meta name="keywords" content="Blue Bird, Express.js, Node.js, Semantic CSS, Performance, Nginx, Redis">
|
|
12
|
+
<meta name="author" content="Seip25">
|
|
13
|
+
<link rel="icon" href="/images/favicon.ico" />
|
|
14
|
+
<link rel="stylesheet" href="/css/bluebird.css" />
|
|
15
|
+
<script src="/js/bluebird.js"></script>
|
|
16
|
+
</head>
|
|
17
|
+
|
|
18
|
+
<body>
|
|
19
|
+
<header>
|
|
20
|
+
<nav>
|
|
21
|
+
<a href="/" class="flex items-center gap-2">
|
|
22
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
|
23
|
+
stroke-linecap="round" stroke-linejoin="round" class="text-blue">
|
|
24
|
+
<path d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
|
25
|
+
</svg>
|
|
26
|
+
<strong class="text-xl text-gradient">Blue Bird</strong>
|
|
27
|
+
</a>
|
|
28
|
+
</nav>
|
|
29
|
+
</header>
|
|
30
|
+
|
|
31
|
+
<main>
|
|
32
|
+
<aside>
|
|
33
|
+
<h4>Menu</h4>
|
|
34
|
+
<a href="/" class="active badge badge-glow px-3 py-3">Home</a>
|
|
35
|
+
<a href="/about">About</a>
|
|
36
|
+
<a href="https://seip25.github.io/Blue-bird-css/" target="_blank">Blue Bird CSS</a>
|
|
37
|
+
<a href="https://github.com/seip25/Blue-bird" target="_blank" role="button"
|
|
38
|
+
class="btn-sm outline">GitHub</a>
|
|
39
|
+
</aside>
|
|
40
|
+
|
|
41
|
+
<div>
|
|
42
|
+
<section class="hero ">
|
|
43
|
+
<h1 class="mt-3 text-gradient font-bold">Hello, Developer!</h1>
|
|
44
|
+
<p class="lead">
|
|
45
|
+
Blue Bird is a fast, structured, and opinionated Express.js framework for developers who want
|
|
46
|
+
visual elegance and performance with raw HTML and CSS.
|
|
47
|
+
</p>
|
|
48
|
+
<div class="hero-actions">
|
|
49
|
+
<a href="/about" role="button">About Us →</a>
|
|
50
|
+
<a href="https://seip25.github.io/Blue-bird-css/" target="_blank" role="button" class="outline">Blue
|
|
51
|
+
Bird CSS Docs</a>
|
|
52
|
+
<a href="https://github.com/seip25/Blue-bird" target="_blank" role="button"
|
|
53
|
+
class="outline">GitHub</a>
|
|
54
|
+
</div>
|
|
55
|
+
</section>
|
|
56
|
+
|
|
57
|
+
<div class="mt-8">
|
|
58
|
+
<div class="grid cols-3 gap-4">
|
|
59
|
+
<article class="hover-glow ">
|
|
60
|
+
<header>
|
|
61
|
+
<span class="badge badge-primary mb-2">Rendering</span>
|
|
62
|
+
<h3 class="text-gradient-blue">Ultra-Fast HTML Render</h3>
|
|
63
|
+
</header>
|
|
64
|
+
<p>Direct static HTML rendering using high-speed string placeholder
|
|
65
|
+
replacement, bypassing heavy
|
|
66
|
+
parsing engines completely.</p>
|
|
67
|
+
</article>
|
|
68
|
+
|
|
69
|
+
<article class="hover-glow ">
|
|
70
|
+
<header>
|
|
71
|
+
<span class="badge badge-primary mb-2">Performance</span>
|
|
72
|
+
<h3 class="text-gradient-blue">In-Memory Caching</h3>
|
|
73
|
+
</header>
|
|
74
|
+
<p>Configurable time-to-live caching. Rendered pages are stored in RAM for
|
|
75
|
+
instant,
|
|
76
|
+
sub-millisecond
|
|
77
|
+
delivery in production.</p>
|
|
78
|
+
</article>
|
|
79
|
+
|
|
80
|
+
<article class="hover-glow ">
|
|
81
|
+
<header>
|
|
82
|
+
<span class="badge badge-primary mb-2">Security</span>
|
|
83
|
+
<h3 class="text-gradient-blue">JWT & Route Security</h3>
|
|
84
|
+
</header>
|
|
85
|
+
<p>Secure cookie management, GCM-encrypted JWT tokens, custom helmet
|
|
86
|
+
configurations, and route
|
|
87
|
+
rate
|
|
88
|
+
limiting built-in.</p>
|
|
89
|
+
</article>
|
|
90
|
+
|
|
91
|
+
<article class="hover-glow ">
|
|
92
|
+
<header>
|
|
93
|
+
<span class="badge badge-primary mb-2">Database</span>
|
|
94
|
+
<h3 class="text-gradient-blue">Unified DB Layer</h3>
|
|
95
|
+
</header>
|
|
96
|
+
<p>Built-in support for SQLite (WAL mode), MySQL, and PostgreSQL with
|
|
97
|
+
connection pooling,
|
|
98
|
+
retries,
|
|
99
|
+
and Redis query caching.</p>
|
|
100
|
+
</article>
|
|
101
|
+
|
|
102
|
+
<article class="hover-glow ">
|
|
103
|
+
<header>
|
|
104
|
+
<span class="badge badge-primary mb-2">DevOps</span>
|
|
105
|
+
<h3 class="text-gradient-blue">Zero-Config Docker</h3>
|
|
106
|
+
</header>
|
|
107
|
+
<p>Containerized development & production orchestration with Nginx
|
|
108
|
+
static delivery and Redis
|
|
109
|
+
caching out of the box.</p>
|
|
110
|
+
</article>
|
|
111
|
+
|
|
112
|
+
<article class="hover-glow ">
|
|
113
|
+
<header>
|
|
114
|
+
<span class="badge badge-primary mb-2">Design</span>
|
|
115
|
+
<h3 class="text-gradient-blue">Pure Semantic CSS</h3>
|
|
116
|
+
</header>
|
|
117
|
+
<p>Clean semantic markup styled by <a href="https://seip25.github.io/Blue-bird-css/"
|
|
118
|
+
target="_blank">Blue Bird CSS</a> with
|
|
119
|
+
zero JavaScript framework bloat or compile steps.
|
|
120
|
+
</p>
|
|
121
|
+
</article>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</main>
|
|
126
|
+
|
|
127
|
+
<footer>
|
|
128
|
+
<div class="footer-content">
|
|
129
|
+
<p>Powered by <strong class="text-gradient">Blue Bird Framework</strong> &
|
|
130
|
+
<strong class="text-gradient-blue">
|
|
131
|
+
<a href="https://seip25.github.io/Blue-bird-css/" target="_blank">Blue Bird
|
|
132
|
+
CSS</a>
|
|
133
|
+
</strong>.
|
|
134
|
+
</p>
|
|
135
|
+
<div class="lang-switcher">
|
|
136
|
+
<a href="/" class="lang-link">Home</a>
|
|
137
|
+
<a href="/about" class="lang-link">About</a>
|
|
138
|
+
<a href="https://github.com/seip25/Blue-bird" target="_blank" class="github-star mt-2">★
|
|
139
|
+
GitHub</a>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</footer>
|
|
143
|
+
</body>
|
|
144
|
+
|
|
145
145
|
</html>
|