one-for-all-framework 4.2.0 → 4.4.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.
- checksums.yaml +4 -4
- data/.env.example +5 -0
- data/Gemfile +2 -0
- data/README.md +16 -1
- data/app/controllers/application_controller.rb +4 -0
- data/app/mailers/application_mailer.rb +42 -0
- data/app/middleware/jwt_middleware.rb +31 -0
- data/app/views/docs.erb +108 -13
- data/app/views/index.erb +1 -1
- data/bin/ofa +235 -10
- data/config/boot.rb +11 -1
- data/db/data.sqlite3 +0 -0
- metadata +17 -2
- data/db/target.sqlite3 +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 345583a2af3a1c7ee4eb6d923c1d87ea769a5f6e70c98537a36575d1ee89dc03
|
|
4
|
+
data.tar.gz: 596e7f2abe8bcf277d180e5044e9b5fb77fbfa894c894c92639d7cda3fc4136a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b5c07c96b28fc73548e35ecbb81113361d6b32575c4a681bf7bf0b916e2c39e4b18e34032f252c83176116a89d4377d8f1f58ee16acab90cdafb4d0ce467b03
|
|
7
|
+
data.tar.gz: bcb67f5526ebca56809ec9fa8009199b0911c2668445b8eb15ecd7f5995edde4fd8b33271b4168a416fc04aa87f768947d8109a1e0816a97b2620571cd1f911d
|
data/.env.example
CHANGED
|
@@ -2,3 +2,8 @@ DATABASE_URL="sqlite://db/development.sqlite3"
|
|
|
2
2
|
CLOUDINARY_URL="cloudinary://API_KEY:API_SECRET@CLOUD_NAME"
|
|
3
3
|
EKS_CENT_SECRET_KEY_BASE="your_secret_key"
|
|
4
4
|
EKS_ENV="development"
|
|
5
|
+
MAILER_FROM="admin@example.com"
|
|
6
|
+
SMTP_ADDRESS="smtp.gmail.com"
|
|
7
|
+
SMTP_PORT=587
|
|
8
|
+
SMTP_USER_NAME="your_email@gmail.com"
|
|
9
|
+
SMTP_PASSWORD="your_app_password"
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="public/images/logo.png" width="500" height="500" alt="OFA Framework Logo">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
# ⚡ One-For-All (OFA) Framework v4.
|
|
5
|
+
# ⚡ One-For-All (OFA) Framework v4.4.0
|
|
6
6
|
|
|
7
7
|
[](https://www.ruby-lang.org/)
|
|
8
8
|
[](LICENSE)
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
- **🔐 Enterprise Ready**: Built-in CSRF protection, secure session management, and input validation.
|
|
22
22
|
- **🌐 Global Support**: Multi-language (I18n) support and SEO optimization ready.
|
|
23
23
|
- **🖋️ Rich Text Editor**: Integrated Trix Editor for seamless post and page creation with image upload support.
|
|
24
|
+
- **📡 Modern API**: Built-in JWT support and automated Swagger/OpenAPI documentation.
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -82,6 +83,8 @@ Boots the high-performance Eksa Server engine.
|
|
|
82
83
|
### 🏗️ Generators (Scaffolding)
|
|
83
84
|
#### `ofa g controller NAME`
|
|
84
85
|
* **Output:** `✅ Created app/controllers/blog_controller.rb`
|
|
86
|
+
#### `ofa g api NAME`
|
|
87
|
+
* **Output:** `✅ Created app/controllers/shop_controller.rb`
|
|
85
88
|
#### `ofa g model NAME`
|
|
86
89
|
* **Output:** `✅ Created app/models/product.rb`
|
|
87
90
|
#### `ofa g post TITLE [args]`
|
|
@@ -89,6 +92,11 @@ Creates a SEO-optimized blog post with metadata.
|
|
|
89
92
|
* **Example:** `./ofa g post "Hello World" --author Antigravity`
|
|
90
93
|
* **Output:** `✅ Created app/views/posts/hello_world.erb`
|
|
91
94
|
|
|
95
|
+
### 📡 API & Documentation
|
|
96
|
+
#### `ofa swagger`
|
|
97
|
+
Generates a standards-compliant `openapi.json` file.
|
|
98
|
+
* **Output:** `✅ Documentation saved to: openapi.json`
|
|
99
|
+
|
|
92
100
|
### 📂 Database Management
|
|
93
101
|
#### `ofa db switch TYPE [URL]`
|
|
94
102
|
Switches your database adapter on the fly.
|
|
@@ -132,6 +140,9 @@ Securely manages admin credentials.
|
|
|
132
140
|
| `ofa console` | **Interactive REPL.** Starts a Ruby console pre-loaded with your application environment and models for testing and debugging. |
|
|
133
141
|
| `ofa doctor` | **System Health Check.** Validates `.env` config, database connectivity (SQL/MongoDB), Ruby version, and dependencies. |
|
|
134
142
|
| `ofa routes` | **Route Inspection.** Lists all registered routes in your application in a clean tabular format. |
|
|
143
|
+
| `ofa swagger` | **OpenAPI Generation.** Auto-generates `openapi.json` for your entire application. |
|
|
144
|
+
| `ofa task NAME` | **Run Background Task.** Executes a task defined in `lib/tasks/`. |
|
|
145
|
+
| `ofa test` | **Run Test Suite.** Executes all unit tests in the `test/` directory using Minitest. |
|
|
135
146
|
| `ofa deploy` | **Production Deployment.** Automatically detects deployment targets (Railway/Docker/Git). |
|
|
136
147
|
|
|
137
148
|
---
|
|
@@ -142,8 +153,12 @@ Automate the creation of boilerplate code with the generator command.
|
|
|
142
153
|
| Command | Description |
|
|
143
154
|
| :--- | :--- |
|
|
144
155
|
| `ofa g controller NAME` | Creates a new controller in `app/controllers/{name}_controller.rb` with a default `index` action. |
|
|
156
|
+
| `ofa g api NAME` | Creates a JSON-based controller in `app/controllers/{name}_controller.rb` inheriting from `ApiController`. |
|
|
145
157
|
| `ofa g model NAME` | Generates a database model in `app/models/{name}.rb` integrated with the Sequel ORM. |
|
|
146
158
|
| `ofa g migration NAME` | Creates a timestamped migration file in `db/migrations/`. Use this to define your schema changes. |
|
|
159
|
+
| `ofa g mailer NAME ACTION` | Generates a new mailer in `app/mailers/` and an ERB template in `app/views/mailers/`. |
|
|
160
|
+
| `ofa g task NAME` | Creates a new background task file in `lib/tasks/`. |
|
|
161
|
+
| `ofa g test NAME` | Generates a new Minitest unit test in `test/`. |
|
|
147
162
|
| `ofa g post TITLE` | Creates a new Markdown/ERB post in `app/views/posts/`. <br> *Args:* `--category`, `--author`, `--image`. <br> *Example:* `./ofa g post "My First Journey" --category Tech --author "John Doe"` |
|
|
148
163
|
|
|
149
164
|
---
|
|
@@ -27,6 +27,10 @@ class ApplicationController
|
|
|
27
27
|
@res.headers['Location'] = path
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def render_json(data, status: 200)
|
|
31
|
+
@res.json(data, status: status)
|
|
32
|
+
end
|
|
33
|
+
|
|
30
34
|
# Validation Helper
|
|
31
35
|
def validate!(required_params)
|
|
32
36
|
missing = required_params.select { |p| params[p.to_s].nil? || params[p.to_s].empty? }
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'mail'
|
|
2
|
+
require 'erb'
|
|
3
|
+
|
|
4
|
+
class ApplicationMailer
|
|
5
|
+
# Configure default settings if needed
|
|
6
|
+
def self.mail(to:, subject:, template:, locals: {})
|
|
7
|
+
# Template name expected: "welcome_mailer/welcome"
|
|
8
|
+
template_path = File.join(APP_ROOT, 'app', 'views', 'mailers', "#{template}.erb")
|
|
9
|
+
|
|
10
|
+
unless File.exist?(template_path)
|
|
11
|
+
puts "❌ Mailer Error: Template not found at #{template_path}"
|
|
12
|
+
return
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Create rendering context
|
|
16
|
+
context = Object.new
|
|
17
|
+
locals.each { |k, v| context.instance_variable_set("@#{k}", v) }
|
|
18
|
+
|
|
19
|
+
body_content = ERB.new(File.read(template_path)).result(context.instance_eval { binding })
|
|
20
|
+
|
|
21
|
+
mail = Mail.new do
|
|
22
|
+
from ENV['MAILER_FROM'] || 'admin@example.com'
|
|
23
|
+
to to
|
|
24
|
+
subject subject
|
|
25
|
+
body body_content
|
|
26
|
+
|
|
27
|
+
# Optional: set content type to HTML if needed
|
|
28
|
+
# content_type 'text/html; charset=UTF-8'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# In development/test, we might want to just log it
|
|
32
|
+
if ENV['EKS_ENV'] == 'production'
|
|
33
|
+
mail.deliver!
|
|
34
|
+
else
|
|
35
|
+
puts "--- 📧 EMAIL SENT ---"
|
|
36
|
+
puts "To: #{to}"
|
|
37
|
+
puts "Subject: #{subject}"
|
|
38
|
+
puts "Body:\n#{body_content}"
|
|
39
|
+
puts "----------------------"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'jwt'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
class JwtMiddleware
|
|
7
|
+
def initialize(app)
|
|
8
|
+
@app = app
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
auth_header = env['HTTP_AUTHORIZATION']
|
|
13
|
+
if auth_header && auth_header.start_with?('Bearer ')
|
|
14
|
+
token = auth_header.split(' ').last
|
|
15
|
+
begin
|
|
16
|
+
secret = ENV['JWT_SECRET'] || 'one-for-all-secret-key'
|
|
17
|
+
if defined?(JWT)
|
|
18
|
+
decoded_token = JWT.decode(token, secret, true, { algorithm: 'HS256' })
|
|
19
|
+
env['current_user_id'] = decoded_token[0]['user_id']
|
|
20
|
+
env['jwt_payload'] = decoded_token[0]
|
|
21
|
+
end
|
|
22
|
+
rescue JWT::DecodeError
|
|
23
|
+
# If token is invalid, we don't halt here, just don't set current_user_id
|
|
24
|
+
rescue NameError
|
|
25
|
+
# JWT not defined
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
@app.call(env)
|
|
30
|
+
end
|
|
31
|
+
end
|
data/app/views/docs.erb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="space-y-12 pb-20">
|
|
2
2
|
<div class="text-left">
|
|
3
|
-
<div class="badge-premium">Documentation v4.
|
|
3
|
+
<div class="badge-premium">Documentation v4.4.0</div>
|
|
4
4
|
<h1 class="text-5xl font-black tracking-tighter mb-4 text-slate-700 dark:text-white">Framework <span class="text-primary">Guide</span></h1>
|
|
5
5
|
<p class="text-xl text-slate-500 max-w-2xl leading-relaxed">Everything you need to know about building premium web applications with the One-For-All framework.</p>
|
|
6
6
|
</div>
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
<a href="#routing" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Routing System</a>
|
|
17
17
|
<a href="#cms" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">CMS & Features</a>
|
|
18
18
|
<a href="#ecommerce" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">E-Commerce</a>
|
|
19
|
+
<a href="#api" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">API & Modern Web</a>
|
|
20
|
+
<a href="#advanced" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Mailers & Background Jobs</a>
|
|
21
|
+
<a href="#testing" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Automated Testing</a>
|
|
19
22
|
<a href="#deploy" class="block px-4 py-2 rounded-xl hover:bg-primary/5 text-slate-600 hover:text-primary transition-all font-semibold border-l-2 border-transparent hover:border-primary">Deployment</a>
|
|
20
23
|
</nav>
|
|
21
24
|
</aside>
|
|
@@ -74,24 +77,44 @@
|
|
|
74
77
|
<td class="px-6 py-4 font-mono text-primary">./ofa routes</td>
|
|
75
78
|
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">List all registered routes in a clean table.</td>
|
|
76
79
|
</tr>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
<tr>
|
|
81
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa deploy</td>
|
|
82
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs italic">Railway/Docker/Git auto-detection for production.</td>
|
|
83
|
+
</tr>
|
|
84
|
+
<tr>
|
|
85
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa swagger</td>
|
|
86
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Generate <code>openapi.json</code> for Swagger UI.</td>
|
|
87
|
+
</tr>
|
|
81
88
|
|
|
82
89
|
<tr class="bg-slate-500/5"><td colspan="2" class="px-6 py-2 text-[10px] font-black uppercase tracking-tighter text-slate-400">Generators (Scaffolding)</td></tr>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
<tr>
|
|
91
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g controller NAME</td>
|
|
92
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Generates <code>app/controllers/{name}_controller.rb</code>.</td>
|
|
93
|
+
</tr>
|
|
94
|
+
<tr>
|
|
95
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g api NAME</td>
|
|
96
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">JSON-based controller inheriting from ApiController.</td>
|
|
97
|
+
</tr>
|
|
87
98
|
<tr>
|
|
88
99
|
<td class="px-6 py-4 font-mono text-primary">./ofa g model NAME</td>
|
|
89
100
|
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Creates model & migration linked to Sequel/NoSQL.</td>
|
|
90
101
|
</tr>
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
<tr>
|
|
103
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g migration NAME</td>
|
|
104
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Creates timestamped migration in <code>db/migrations/</code>.</td>
|
|
105
|
+
</tr>
|
|
106
|
+
<tr>
|
|
107
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g mailer NAME</td>
|
|
108
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Generates mailer class and ERB templates.</td>
|
|
109
|
+
</tr>
|
|
110
|
+
<tr>
|
|
111
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g task NAME</td>
|
|
112
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Creates a background task in <code>lib/tasks/</code>.</td>
|
|
113
|
+
</tr>
|
|
114
|
+
<tr>
|
|
115
|
+
<td class="px-6 py-4 font-mono text-primary">./ofa g test NAME</td>
|
|
116
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400 text-xs">Generates a Minitest unit test file.</td>
|
|
117
|
+
</tr>
|
|
95
118
|
|
|
96
119
|
<tr class="bg-slate-500/5"><td colspan="2" class="px-6 py-2 text-[10px] font-black uppercase tracking-tighter text-slate-400">Customization</td></tr>
|
|
97
120
|
<tr>
|
|
@@ -310,6 +333,78 @@ resources :posts
|
|
|
310
333
|
</div>
|
|
311
334
|
</section>
|
|
312
335
|
|
|
336
|
+
<!-- API & Modern Web -->
|
|
337
|
+
<section id="api" class="glass-panel p-8 md:p-12 text-left bg-gradient-to-br from-primary/10 to-transparent">
|
|
338
|
+
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
|
339
|
+
<i class="fas fa-network-wired text-primary"></i> API & Modern Web
|
|
340
|
+
</h2>
|
|
341
|
+
<div class="markdown-content space-y-8">
|
|
342
|
+
<div>
|
|
343
|
+
<h4 class="font-bold text-lg mb-2">📡 JSON API Support</h4>
|
|
344
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Build high-performance backends for mobile apps or SPAs. OFA provides a specialized <code>ApiController</code> and JSON rendering helpers.</p>
|
|
345
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-4 font-mono text-sm">
|
|
346
|
+
./ofa g api order
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
<div>
|
|
351
|
+
<h4 class="font-bold text-lg mb-2">🔐 JWT Authentication</h4>
|
|
352
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Decoupled frontends require token-based auth. OFA automatically generates a <code>JWT_SECRET</code> during initialization and includes a <code>JwtMiddleware</code> to verify incoming requests.</p>
|
|
353
|
+
<p class="text-[10px] text-slate-500 mt-2 italic">Note: Tokens are expected in the 'Authorization: Bearer <TOKEN>' header.</p>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<div>
|
|
357
|
+
<h4 class="font-bold text-lg mb-2">📝 Swagger/OpenAPI Documentation</h4>
|
|
358
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Keep your API documentation in sync with your code. The <code>ofa swagger</code> command scans your routes and generates a <code>openapi.json</code> file.</p>
|
|
359
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-4 font-mono text-sm">
|
|
360
|
+
./ofa swagger
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</section>
|
|
365
|
+
|
|
366
|
+
<!-- Advanced Features -->
|
|
367
|
+
<section id="advanced" class="glass-panel p-8 md:p-12 text-left bg-gradient-to-br from-primary/10 to-transparent">
|
|
368
|
+
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
|
369
|
+
<i class="fas fa-envelope text-primary"></i> Mailers & Tasks
|
|
370
|
+
</h2>
|
|
371
|
+
<div class="markdown-content space-y-8">
|
|
372
|
+
<div>
|
|
373
|
+
<h4 class="font-bold text-lg mb-2">📧 Email Mailers</h4>
|
|
374
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Send transactional emails with ease using ERB templates. Mailers are stored in <code>app/mailers</code> and templates in <code>app/views/mailers</code>.</p>
|
|
375
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-4 font-mono text-sm">
|
|
376
|
+
./ofa g mailer welcome signup
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<div>
|
|
381
|
+
<h4 class="font-bold text-lg mb-2">⚙️ Background Tasks</h4>
|
|
382
|
+
<p class="text-sm text-slate-600 dark:text-slate-400">Run long-running processes or scheduled jobs using the Task runner. Defined in <code>lib/tasks/</code>.</p>
|
|
383
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-4 font-mono text-sm">
|
|
384
|
+
./ofa g task sync_data<br>
|
|
385
|
+
./ofa task sync_data
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
</section>
|
|
390
|
+
|
|
391
|
+
<!-- Testing -->
|
|
392
|
+
<section id="testing" class="glass-panel p-8 md:p-12 text-left">
|
|
393
|
+
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
|
394
|
+
<i class="fas fa-vial text-primary"></i> Automated Testing
|
|
395
|
+
</h2>
|
|
396
|
+
<div class="markdown-content">
|
|
397
|
+
<p class="text-slate-600 dark:text-slate-400 mb-6">OFA integrates <strong>Minitest</strong> out-of-the-box for reliable application testing. Run all your tests with a single command.</p>
|
|
398
|
+
<div class="p-4 bg-slate-900 rounded-2xl font-mono text-sm text-green-400 mb-6">
|
|
399
|
+
./ofa test
|
|
400
|
+
</div>
|
|
401
|
+
<p class="text-sm text-slate-500">To create a new test file:</p>
|
|
402
|
+
<div class="p-4 bg-white/5 border border-white/10 rounded-2xl mt-2 font-mono text-sm">
|
|
403
|
+
./ofa g test user_profile
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</section>
|
|
407
|
+
|
|
313
408
|
<!-- Deployment -->
|
|
314
409
|
<section id="deploy" class="glass-panel p-8 md:p-12 text-left border-primary/20">
|
|
315
410
|
<h2 class="text-3xl font-black tracking-tight mb-6 flex items-center gap-3">
|
data/app/views/index.erb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="text-center space-y-6 max-w-2xl mx-auto py-12">
|
|
2
2
|
<div class="inline-flex items-center px-4 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider animate-pulse">
|
|
3
|
-
Framework Version 4.
|
|
3
|
+
Framework Version 4.4.0
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
6
|
<h1 class="text-5xl md:text-7xl font-black tracking-tight leading-tight">
|
data/bin/ofa
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
+
# --- Bundler Setup ---
|
|
4
|
+
# This ensures that if we are in a project with a Gemfile, we use the correct
|
|
5
|
+
# gem versions specified in Gemfile.lock, preventing conflicts with system gems.
|
|
6
|
+
if File.exist?(File.join(Dir.pwd, 'Gemfile'))
|
|
7
|
+
begin
|
|
8
|
+
require 'bundler/setup'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
# Bundler not installed, proceed without it
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
3
14
|
require 'fileutils'
|
|
4
15
|
require 'optparse'
|
|
5
16
|
require 'json'
|
|
@@ -22,7 +33,7 @@ def help
|
|
|
22
33
|
puts " / __ \\/ ____/ / | "
|
|
23
34
|
puts " / / / / /_ / /| | Framework "
|
|
24
35
|
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
25
|
-
puts "\\____/_/ /_/ |_| v4.
|
|
36
|
+
puts "\\____/_/ /_/ |_| v4.4.0 "
|
|
26
37
|
puts " "
|
|
27
38
|
puts "✨ One-For-All Framework CLI ✨"
|
|
28
39
|
puts "-----------------------------"
|
|
@@ -30,8 +41,12 @@ def help
|
|
|
30
41
|
puts " ofa new NAME [TYPE] - Create a new project + automatic bundle install"
|
|
31
42
|
puts " ofa init [TYPE] - Initialize project in current folder"
|
|
32
43
|
puts " ofa g controller NAME - Generate a new controller"
|
|
44
|
+
puts " ofa g api NAME - Generate a new API controller (JSON)"
|
|
33
45
|
puts " ofa g model NAME - Generate a new model"
|
|
34
46
|
puts " ofa g migration NAME - Generate a new migration"
|
|
47
|
+
puts " ofa g mailer NAME ACTION - Generate a new mailer and template"
|
|
48
|
+
puts " ofa g task NAME - Generate a new background task"
|
|
49
|
+
puts " ofa g test NAME - Generate a new unit test"
|
|
35
50
|
puts " ofa g post TITLE - Create a new post [args: --category, --author, --image]"
|
|
36
51
|
puts " ofa feature ACTION F - Toggle features: action=enable/disable, F=cms/auth/rich_text"
|
|
37
52
|
puts " ofa type NAME - Set application type: portfolio, blog, landing_page, e_commerce"
|
|
@@ -45,6 +60,9 @@ def help
|
|
|
45
60
|
puts " ofa console - Start interactive REPL session"
|
|
46
61
|
puts " ofa doctor - Check system health (DB, config, gems)"
|
|
47
62
|
puts " ofa routes - List all registered routes"
|
|
63
|
+
puts " ofa swagger - Auto-generate OpenAPI/Swagger documentation"
|
|
64
|
+
puts " ofa task NAME - Run a background task"
|
|
65
|
+
puts " ofa test - Run all application tests"
|
|
48
66
|
puts " ofa run - Start the application server"
|
|
49
67
|
puts " ofa deploy - Deploy project to production"
|
|
50
68
|
puts "-----------------------------"
|
|
@@ -229,7 +247,7 @@ when 'init'
|
|
|
229
247
|
puts " / __ \\/ ____/ / | "
|
|
230
248
|
puts " / / / / /_ / /| | Framework "
|
|
231
249
|
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
232
|
-
puts "\\____/_/ /_/ |_| v4.
|
|
250
|
+
puts "\\____/_/ /_/ |_| v4.4.0 "
|
|
233
251
|
puts " "
|
|
234
252
|
puts "Initializing One-For-All project as '#{app_type}' in #{PROJECT_ROOT}..."
|
|
235
253
|
|
|
@@ -260,7 +278,7 @@ when 'init'
|
|
|
260
278
|
puts "-----------------------------\n"
|
|
261
279
|
|
|
262
280
|
# Create directories
|
|
263
|
-
dirs = ['app/controllers', 'app/models', 'app/views', 'config', 'db/migrations', 'public/css', 'public/js', 'public/images', 'public/images/uploads']
|
|
281
|
+
dirs = ['app/controllers', 'app/models', 'app/views', 'app/mailers', 'lib/tasks', 'test', 'config', 'db/migrations', 'public/css', 'public/js', 'public/images', 'public/images/uploads']
|
|
264
282
|
dirs.each { |dir| FileUtils.mkdir_p(File.join(PROJECT_ROOT, dir)) }
|
|
265
283
|
|
|
266
284
|
# Copy framework core migrations
|
|
@@ -272,11 +290,27 @@ when 'init'
|
|
|
272
290
|
end
|
|
273
291
|
end
|
|
274
292
|
|
|
293
|
+
# Copy framework core helpers and test assets
|
|
294
|
+
['lib/task_helper.rb', 'app/mailers/application_mailer.rb', 'test/test_helper.rb'].each do |file|
|
|
295
|
+
src = File.join(FRAMEWORK_ROOT, file)
|
|
296
|
+
dest = File.join(PROJECT_ROOT, file)
|
|
297
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
298
|
+
if File.exist?(src)
|
|
299
|
+
content = File.read(src)
|
|
300
|
+
# Inject FRAMEWORK_ROOT into test_helper.rb
|
|
301
|
+
if file == 'test/test_helper.rb'
|
|
302
|
+
content.gsub!("require_relative '../config/boot'", "$LOAD_PATH.unshift '#{FRAMEWORK_ROOT}'\nrequire 'config/boot'")
|
|
303
|
+
end
|
|
304
|
+
File.write(dest, content)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
275
308
|
# Create .env
|
|
276
309
|
env_content = []
|
|
277
310
|
env_content << "EKS_ENV=development"
|
|
278
311
|
env_content << "DATABASE_URL=#{db_url}" if db_type == 'mongodb'
|
|
279
312
|
env_content << "CLOUDINARY_URL=#{cloudinary_url}" if img_storage == 'cloudinary'
|
|
313
|
+
env_content << "JWT_SECRET=#{Array.new(32){[*'0'..'9',*'a'..'z',*'A'..'Z'].sample}.join}"
|
|
280
314
|
File.write(File.join(PROJECT_ROOT, '.env'), env_content.join("\n"))
|
|
281
315
|
|
|
282
316
|
# Create config.ru
|
|
@@ -294,6 +328,7 @@ when 'init'
|
|
|
294
328
|
use EksCent::Middleware::ShowExceptions
|
|
295
329
|
use EksCent::Middleware::Session, secret: EksCent.secret_key_base
|
|
296
330
|
use AuthMiddleware
|
|
331
|
+
use JwtMiddleware
|
|
297
332
|
use CSRFMiddleware
|
|
298
333
|
use EksCent::Middleware::Static, root: 'public'
|
|
299
334
|
use EksCent::Middleware::Logger
|
|
@@ -317,6 +352,8 @@ when 'init'
|
|
|
317
352
|
gem 'mysql2'
|
|
318
353
|
gem 'kramdown'
|
|
319
354
|
gem 'kramdown-parser-gfm'
|
|
355
|
+
gem 'mail'
|
|
356
|
+
gem 'minitest'
|
|
320
357
|
RUBY
|
|
321
358
|
File.write(File.join(PROJECT_ROOT, 'Gemfile'), gemfile_content)
|
|
322
359
|
|
|
@@ -463,11 +500,90 @@ when 'g'
|
|
|
463
500
|
class #{class_name} < ApplicationController
|
|
464
501
|
def index
|
|
465
502
|
# Logic for #{name}
|
|
503
|
+
render_json({ message: "Welcome to #{class_name}" })
|
|
466
504
|
end
|
|
467
505
|
end
|
|
468
506
|
RUBY
|
|
469
507
|
File.write(file_name, content)
|
|
470
508
|
puts "Created: #{file_name}"
|
|
509
|
+
elsif sub == 'api' && name
|
|
510
|
+
class_name = name.capitalize + "Controller"
|
|
511
|
+
file_name = File.join(PROJECT_ROOT, "app/controllers/#{name.downcase}_controller.rb")
|
|
512
|
+
content = <<~RUBY
|
|
513
|
+
require_relative 'api_controller'
|
|
514
|
+
|
|
515
|
+
class #{class_name} < ApiController
|
|
516
|
+
# GET /api/#{name.downcase}
|
|
517
|
+
def index
|
|
518
|
+
render_json({
|
|
519
|
+
status: 'success',
|
|
520
|
+
message: "API endpoint for #{name} ready",
|
|
521
|
+
data: []
|
|
522
|
+
})
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# POST /api/#{name.downcase}
|
|
526
|
+
def create
|
|
527
|
+
# TODO: Implement creation logic
|
|
528
|
+
render_json({ status: 'success', message: "#{name} created" }, status: 201)
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
RUBY
|
|
532
|
+
File.write(file_name, content)
|
|
533
|
+
puts "Created API Controller: #{file_name}"
|
|
534
|
+
puts "💡 Don't forget to register it in config/routes.rb"
|
|
535
|
+
elsif sub == 'mailer' && name
|
|
536
|
+
action = ARGV.shift || 'welcome'
|
|
537
|
+
class_name = name.capitalize + "Mailer"
|
|
538
|
+
file_name = File.join(PROJECT_ROOT, "app/mailers/#{name.downcase}_mailer.rb")
|
|
539
|
+
content = <<~RUBY
|
|
540
|
+
class #{class_name} < ApplicationMailer
|
|
541
|
+
def #{action}(user)
|
|
542
|
+
mail(
|
|
543
|
+
to: user.email,
|
|
544
|
+
subject: "Welcome to our platform!",
|
|
545
|
+
template: "#{name.downcase}_mailer/#{action}",
|
|
546
|
+
locals: { user: user }
|
|
547
|
+
)
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
RUBY
|
|
551
|
+
File.write(file_name, content)
|
|
552
|
+
|
|
553
|
+
# Create template
|
|
554
|
+
template_dir = File.join(PROJECT_ROOT, "app/views/mailers/#{name.downcase}_mailer")
|
|
555
|
+
FileUtils.mkdir_p(template_dir)
|
|
556
|
+
template_file = File.join(template_dir, "#{action}.erb")
|
|
557
|
+
File.write(template_file, "<h1>Hello <%= @user.username %></h1>\n<p>Welcome to One-For-All Framework!</p>")
|
|
558
|
+
|
|
559
|
+
puts "Created Mailer: #{file_name}"
|
|
560
|
+
puts "Created Template: #{template_file}"
|
|
561
|
+
elsif sub == 'task' && name
|
|
562
|
+
dir_path = File.join(PROJECT_ROOT, "lib/tasks")
|
|
563
|
+
FileUtils.mkdir_p(dir_path)
|
|
564
|
+
file_name = File.join(dir_path, "#{name.downcase}.rb")
|
|
565
|
+
content = <<~RUBY
|
|
566
|
+
desc "Description for #{name} task"
|
|
567
|
+
task :#{name.downcase} do
|
|
568
|
+
puts "Running task: #{name}"
|
|
569
|
+
# Your background job logic here
|
|
570
|
+
end
|
|
571
|
+
RUBY
|
|
572
|
+
File.write(file_name, content)
|
|
573
|
+
puts "Created Task: #{file_name}"
|
|
574
|
+
elsif sub == 'test' && name
|
|
575
|
+
file_name = File.join(PROJECT_ROOT, "test/#{name.downcase}_test.rb")
|
|
576
|
+
content = <<~RUBY
|
|
577
|
+
require_relative 'test_helper'
|
|
578
|
+
|
|
579
|
+
class #{name.capitalize}Test < Minitest::Test
|
|
580
|
+
def test_example
|
|
581
|
+
assert true
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
RUBY
|
|
585
|
+
File.write(file_name, content)
|
|
586
|
+
puts "Created Test: #{file_name}"
|
|
471
587
|
elsif sub == 'model' && name
|
|
472
588
|
class_name = name.capitalize
|
|
473
589
|
file_name = File.join(PROJECT_ROOT, "app/models/#{name.downcase}.rb")
|
|
@@ -504,7 +620,7 @@ when 'g'
|
|
|
504
620
|
File.write(file_name, content)
|
|
505
621
|
puts "Created post: #{file_name}"
|
|
506
622
|
else
|
|
507
|
-
puts "Usage: ofa g [controller|model|migration|post] NAME"
|
|
623
|
+
puts "Usage: ofa g [controller|api|model|migration|mailer|task|test|post] NAME"
|
|
508
624
|
end
|
|
509
625
|
|
|
510
626
|
when 'feature'
|
|
@@ -514,7 +630,8 @@ when 'feature'
|
|
|
514
630
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
515
631
|
config = JSON.parse(File.read(config_path))
|
|
516
632
|
config[feature] = (action == 'enable')
|
|
517
|
-
File.
|
|
633
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
634
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
518
635
|
puts "Feature '#{feature}' has been #{action}d."
|
|
519
636
|
|
|
520
637
|
if action == 'enable' && feature == 'cms' && !config['auth']
|
|
@@ -533,7 +650,8 @@ when 'type'
|
|
|
533
650
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
534
651
|
config = JSON.parse(File.read(config_path))
|
|
535
652
|
config['type'] = name
|
|
536
|
-
File.
|
|
653
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
654
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
537
655
|
puts "Application type set to '#{name}'."
|
|
538
656
|
|
|
539
657
|
when 'theme'
|
|
@@ -546,7 +664,8 @@ when 'theme'
|
|
|
546
664
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
547
665
|
config = JSON.parse(File.read(config_path))
|
|
548
666
|
config['theme'] = name
|
|
549
|
-
File.
|
|
667
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
668
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
550
669
|
puts "Application theme set to '#{name}'."
|
|
551
670
|
|
|
552
671
|
when 'storage'
|
|
@@ -609,7 +728,8 @@ when 'db'
|
|
|
609
728
|
env_lines = File.exist?(env_path) ? File.readlines(env_path) : []
|
|
610
729
|
env_lines.reject! { |l| l.start_with?('DATABASE_URL=') }
|
|
611
730
|
env_lines << "DATABASE_URL=#{db_name}\n"
|
|
612
|
-
File.
|
|
731
|
+
File.open("#{env_path}.tmp", 'w') { |f| f.write(env_lines.join) }
|
|
732
|
+
File.rename("#{env_path}.tmp", env_path)
|
|
613
733
|
puts "✅ Connection string saved to .env"
|
|
614
734
|
end
|
|
615
735
|
when 'sqlite'
|
|
@@ -617,7 +737,8 @@ when 'db'
|
|
|
617
737
|
else
|
|
618
738
|
config = { "adapter" => type, "host" => "localhost", "user" => "root", "password" => "", "database" => db_name || "one_for_all" }
|
|
619
739
|
end
|
|
620
|
-
File.
|
|
740
|
+
File.open("#{config_path}.tmp", 'w') { |f| f.write(JSON.pretty_generate(config)) }
|
|
741
|
+
File.rename("#{config_path}.tmp", config_path)
|
|
621
742
|
puts "Switched to #{type} mode."
|
|
622
743
|
when 'migrate'
|
|
623
744
|
run_migrations
|
|
@@ -702,7 +823,7 @@ when 'console'
|
|
|
702
823
|
puts " / __ \\/ ____/ / | "
|
|
703
824
|
puts " / / / / /_ / /| | Framework "
|
|
704
825
|
puts "/ /_/ / __/ / ___ | Console (REPL) "
|
|
705
|
-
puts "\\____/_/ /_/ |_| v4.
|
|
826
|
+
puts "\\____/_/ /_/ |_| v4.4.0 "
|
|
706
827
|
puts " "
|
|
707
828
|
puts "✨ Loading environment... (Type 'exit' to quit)"
|
|
708
829
|
|
|
@@ -736,6 +857,53 @@ when 'routes'
|
|
|
736
857
|
end
|
|
737
858
|
puts "--------------------------------------------------------"
|
|
738
859
|
|
|
860
|
+
when 'swagger'
|
|
861
|
+
ensure_initialized!
|
|
862
|
+
puts "📝 Generating OpenAPI 3.0 documentation..."
|
|
863
|
+
|
|
864
|
+
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
865
|
+
ENV['EKS_ENV'] = 'test'
|
|
866
|
+
require File.join(FRAMEWORK_ROOT, 'config', 'boot')
|
|
867
|
+
|
|
868
|
+
openapi = {
|
|
869
|
+
openapi: "3.0.0",
|
|
870
|
+
info: {
|
|
871
|
+
title: "#{File.basename(PROJECT_ROOT)} API",
|
|
872
|
+
version: "1.0.0",
|
|
873
|
+
description: "Auto-generated by One-For-All Framework"
|
|
874
|
+
},
|
|
875
|
+
paths: {}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if defined?(ROUTES)
|
|
879
|
+
routes_hash = ROUTES.instance_variable_get(:@routes)
|
|
880
|
+
routes_hash.each do |verb, list|
|
|
881
|
+
list.each do |r|
|
|
882
|
+
path = r[:path].gsub(/:(\w+)/, '{\1}') # Convert :id to {id}
|
|
883
|
+
openapi[:paths][path] ||= {}
|
|
884
|
+
openapi[:paths][path][verb.to_s.downcase] = {
|
|
885
|
+
summary: "Endpoint for #{path}",
|
|
886
|
+
responses: {
|
|
887
|
+
"200" => { description: "Successful response" }
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
# Add parameters if dynamic path
|
|
892
|
+
if path.include?('{')
|
|
893
|
+
params = path.scan(/\{(\w+)\}/).flatten
|
|
894
|
+
openapi[:paths][path][verb.to_s.downcase][:parameters] = params.map do |p|
|
|
895
|
+
{ name: p, in: "path", required: true, schema: { type: "string" } }
|
|
896
|
+
end
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
output_path = File.join(PROJECT_ROOT, 'openapi.json')
|
|
903
|
+
File.write(output_path, JSON.pretty_generate(openapi))
|
|
904
|
+
puts "✅ Documentation saved to: #{output_path}"
|
|
905
|
+
puts "💡 You can paste this into https://editor.swagger.io to view it."
|
|
906
|
+
|
|
739
907
|
when 'run'
|
|
740
908
|
ensure_initialized!
|
|
741
909
|
# Check for config.ru
|
|
@@ -767,6 +935,63 @@ when 'deploy'
|
|
|
767
935
|
puts " This framework supports deployment via Docker or Git Push."
|
|
768
936
|
end
|
|
769
937
|
|
|
938
|
+
when 'task'
|
|
939
|
+
ensure_initialized!
|
|
940
|
+
name = ARGV.shift
|
|
941
|
+
if name.nil?
|
|
942
|
+
puts "Usage: ofa task NAME"
|
|
943
|
+
# List available tasks
|
|
944
|
+
tasks_dir = File.join(PROJECT_ROOT, 'lib/tasks')
|
|
945
|
+
if Dir.exist?(tasks_dir)
|
|
946
|
+
puts "\nAvailable tasks:"
|
|
947
|
+
Dir.glob(File.join(tasks_dir, '*.rb')).each do |f|
|
|
948
|
+
puts " - #{File.basename(f, '.rb')}"
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
exit 1
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
# Load framework & app
|
|
955
|
+
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
956
|
+
require File.join(FRAMEWORK_ROOT, 'config', 'boot')
|
|
957
|
+
require File.join(FRAMEWORK_ROOT, 'lib', 'task_helper')
|
|
958
|
+
|
|
959
|
+
# Load all tasks in lib/tasks
|
|
960
|
+
Dir.glob(File.join(PROJECT_ROOT, 'lib/tasks/*.rb')).each { |f| require f }
|
|
961
|
+
|
|
962
|
+
run_task(name)
|
|
963
|
+
|
|
964
|
+
when 'test'
|
|
965
|
+
ensure_initialized!
|
|
966
|
+
puts "🧪 Running tests..."
|
|
967
|
+
puts "------------------------------------------"
|
|
968
|
+
|
|
969
|
+
# Set environment to test
|
|
970
|
+
ENV['EKS_ENV'] = 'test'
|
|
971
|
+
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
972
|
+
|
|
973
|
+
# Add project root to load path
|
|
974
|
+
$LOAD_PATH.unshift PROJECT_ROOT
|
|
975
|
+
$LOAD_PATH.unshift File.join(PROJECT_ROOT, 'test')
|
|
976
|
+
|
|
977
|
+
# Find all tests (support both _test.rb and _spec.rb)
|
|
978
|
+
if ARGV.empty?
|
|
979
|
+
test_files = Dir.glob(File.join(PROJECT_ROOT, 'test/**/{*_test.rb,*_spec.rb}'))
|
|
980
|
+
else
|
|
981
|
+
test_files = ARGV.map { |arg| File.expand_path(arg, PROJECT_ROOT) }
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
if test_files.empty?
|
|
985
|
+
puts "❌ No tests found in test/ directory."
|
|
986
|
+
exit 1
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
# Run them by requiring
|
|
990
|
+
test_files.each { |f| require f }
|
|
991
|
+
|
|
992
|
+
# Trigger EksaMination execution if available
|
|
993
|
+
EksaMination.run if defined?(EksaMination)
|
|
994
|
+
|
|
770
995
|
else
|
|
771
996
|
help
|
|
772
997
|
end
|
data/config/boot.rb
CHANGED
|
@@ -4,6 +4,10 @@ Bundler.require(:default)
|
|
|
4
4
|
require 'eks-cent'
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'kramdown'
|
|
7
|
+
begin
|
|
8
|
+
require 'jwt'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
end
|
|
7
11
|
|
|
8
12
|
# Basic project structure constants
|
|
9
13
|
APP_ROOT ||= File.expand_path('..', __dir__)
|
|
@@ -75,6 +79,12 @@ module EksCent
|
|
|
75
79
|
@headers['Content-Type'] ||= 'text/html'
|
|
76
80
|
@body << result
|
|
77
81
|
end
|
|
82
|
+
|
|
83
|
+
def json(data, status: 200)
|
|
84
|
+
@status = status
|
|
85
|
+
@headers['Content-Type'] = 'application/json'
|
|
86
|
+
@body << data.to_json
|
|
87
|
+
end
|
|
78
88
|
end
|
|
79
89
|
end
|
|
80
90
|
|
|
@@ -83,7 +93,7 @@ require_relative 'database'
|
|
|
83
93
|
|
|
84
94
|
# Autoloading (Framework Core first, then APP_ROOT)
|
|
85
95
|
framework_app = File.expand_path('../app', __dir__)
|
|
86
|
-
['controllers', 'models', 'middleware', 'helpers'].each do |folder|
|
|
96
|
+
['controllers', 'models', 'middleware', 'helpers', 'mailers'].each do |folder|
|
|
87
97
|
next if folder == 'models' && ENV['SKIP_MODELS']
|
|
88
98
|
loaded = []
|
|
89
99
|
# Load framework core
|
data/db/data.sqlite3
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: one-for-all-framework
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ishikawa Uta
|
|
@@ -169,6 +169,20 @@ dependencies:
|
|
|
169
169
|
- - "~>"
|
|
170
170
|
- !ruby/object:Gem::Version
|
|
171
171
|
version: '1.1'
|
|
172
|
+
- !ruby/object:Gem::Dependency
|
|
173
|
+
name: jwt
|
|
174
|
+
requirement: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - "~>"
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '2.10'
|
|
179
|
+
type: :runtime
|
|
180
|
+
prerelease: false
|
|
181
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
182
|
+
requirements:
|
|
183
|
+
- - "~>"
|
|
184
|
+
- !ruby/object:Gem::Version
|
|
185
|
+
version: '2.10'
|
|
172
186
|
description: One-For-All is a high-performance Ruby web framework built for speed
|
|
173
187
|
and aesthetics. It features a built-in CMS, premium Glassmorphism design, and supports
|
|
174
188
|
multiple databases including SQLite, MySQL, and MongoDB.
|
|
@@ -196,8 +210,10 @@ files:
|
|
|
196
210
|
- app/controllers/products_controller.rb
|
|
197
211
|
- app/controllers/projects_controller.rb
|
|
198
212
|
- app/helpers/cloudinary_helper.rb
|
|
213
|
+
- app/mailers/application_mailer.rb
|
|
199
214
|
- app/middleware/auth_middleware.rb
|
|
200
215
|
- app/middleware/csrf_middleware.rb
|
|
216
|
+
- app/middleware/jwt_middleware.rb
|
|
201
217
|
- app/models/page.rb
|
|
202
218
|
- app/models/post.rb
|
|
203
219
|
- app/models/product.rb
|
|
@@ -239,7 +255,6 @@ files:
|
|
|
239
255
|
- db/data.sqlite3
|
|
240
256
|
- db/development.sqlite3
|
|
241
257
|
- db/migrations/20260502000000_create_products.rb
|
|
242
|
-
- db/target.sqlite3
|
|
243
258
|
- ofa
|
|
244
259
|
- public/css/cms.css
|
|
245
260
|
- public/images/logo.jpg
|
data/db/target.sqlite3
DELETED
|
Binary file
|