one-for-all-framework 2.0.0 → 4.0.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/README.md +136 -31
- data/app/controllers/cart_controller.rb +52 -0
- data/app/controllers/dashboard_controller.rb +2 -1
- data/app/controllers/products_controller.rb +58 -0
- data/app/models/product.rb +8 -0
- data/app/views/cart_index.erb +112 -0
- data/app/views/cms/pages_form.erb +1 -1
- data/app/views/cms/pages_index.erb +61 -45
- data/app/views/cms/posts_form.erb +1 -1
- data/app/views/cms/posts_index.erb +54 -41
- data/app/views/cms/products_form.erb +89 -0
- data/app/views/cms/products_index.erb +129 -0
- data/app/views/cms/projects_form.erb +8 -8
- data/app/views/cms/projects_index.erb +47 -37
- data/app/views/dashboard.erb +18 -0
- data/app/views/docs.erb +197 -49
- data/app/views/index.erb +1 -1
- data/app/views/layout.erb +26 -7
- data/app/views/product_show.erb +42 -0
- data/app/views/products_index.erb +40 -0
- data/bin/ofa +145 -6
- data/config/boot.rb +1 -0
- data/config/database.json +1 -2
- data/config/database.rb +16 -0
- data/config/routes.rb +32 -0
- data/db/data.sqlite3 +0 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrations/20260502000000_create_products.rb +17 -0
- data/db/target.sqlite3 +0 -0
- metadata +11 -1
data/bin/ofa
CHANGED
|
@@ -22,7 +22,7 @@ def help
|
|
|
22
22
|
puts " / __ \\/ ____/ / | "
|
|
23
23
|
puts " / / / / /_ / /| | Framework "
|
|
24
24
|
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
25
|
-
puts "\\____/_/ /_/ |_|
|
|
25
|
+
puts "\\____/_/ /_/ |_| v4.0.0 "
|
|
26
26
|
puts " "
|
|
27
27
|
puts "✨ One-For-All Framework CLI ✨"
|
|
28
28
|
puts "-----------------------------"
|
|
@@ -34,11 +34,12 @@ def help
|
|
|
34
34
|
puts " ofa g migration NAME - Generate a new migration"
|
|
35
35
|
puts " ofa g post TITLE - Create a new post [args: --category, --author, --image]"
|
|
36
36
|
puts " ofa feature ACTION F - Toggle features: action=enable/disable, F=cms/auth"
|
|
37
|
-
puts " ofa type NAME - Set application type: portfolio, blog, landing_page"
|
|
37
|
+
puts " ofa type NAME - Set application type: portfolio, blog, landing_page, e_commerce"
|
|
38
38
|
puts " ofa theme NAME - Set UI theme: light_glass, dark_glass, cyber_sidebar, retro_terminal, light_sidebar"
|
|
39
39
|
puts " ofa storage NAME - Set image storage: local, cloudinary"
|
|
40
40
|
puts " ofa reset-password USR PWD - Reset admin account password"
|
|
41
41
|
puts " ofa db switch TYPE [NAME] - Switch DB: sqlite, mysql, mariadb, mongodb, postgres"
|
|
42
|
+
puts " ofa db migrate-data TYPE [NAME] - Migrate data to another DB"
|
|
42
43
|
puts " ofa db migrate - Run database migrations"
|
|
43
44
|
puts " ofa migrate - Run database migrations (alias for db migrate)"
|
|
44
45
|
puts " ofa run - Start the application server"
|
|
@@ -49,6 +50,7 @@ end
|
|
|
49
50
|
|
|
50
51
|
def run_migrations
|
|
51
52
|
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
53
|
+
ENV['SKIP_MODELS'] = '1'
|
|
52
54
|
require File.join(FRAMEWORK_ROOT, 'config', 'boot')
|
|
53
55
|
puts "Running migrations..."
|
|
54
56
|
migration_path = File.join(PROJECT_ROOT, "db/migrations")
|
|
@@ -66,6 +68,117 @@ def run_migrations
|
|
|
66
68
|
end
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
def perform_db_migration(target_type, target_name)
|
|
72
|
+
# 1. Initialize source environment
|
|
73
|
+
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
74
|
+
require File.join(FRAMEWORK_ROOT, 'config', 'boot')
|
|
75
|
+
source_db = DB
|
|
76
|
+
|
|
77
|
+
puts "📦 Starting data migration: #{DB_CONFIG['adapter']} -> #{target_type}..."
|
|
78
|
+
|
|
79
|
+
# 2. Setup target connection
|
|
80
|
+
target_db = nil
|
|
81
|
+
target_mongo = nil
|
|
82
|
+
|
|
83
|
+
if ['mongodb', 'mongo'].include?(target_type)
|
|
84
|
+
require 'mongo'
|
|
85
|
+
target_mongo = Mongo::Client.new(target_name)
|
|
86
|
+
target_db = Sequel.connect("sqlite://db/data.sqlite3")
|
|
87
|
+
elsif target_type == 'sqlite'
|
|
88
|
+
target_db = Sequel.connect("sqlite://#{target_name || 'db/production.sqlite3'}")
|
|
89
|
+
else
|
|
90
|
+
target_db = Sequel.connect("#{target_type}://root:@localhost/#{target_name}")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# 3. Ensure target tables exist
|
|
94
|
+
if target_db
|
|
95
|
+
target_db.extension :pagination
|
|
96
|
+
# Re-use the schema creation logic by mimicking a mini-boot
|
|
97
|
+
# For simplicity, we'll just create core tables if they don't exist
|
|
98
|
+
[:users, :pages, :posts, :projects, :products].each do |table|
|
|
99
|
+
unless target_db.table_exists?(table)
|
|
100
|
+
# Copy schema from source (very basic approach)
|
|
101
|
+
schema = source_db.schema(table)
|
|
102
|
+
target_db.create_table(table) do
|
|
103
|
+
schema.each do |col_name, col_info|
|
|
104
|
+
column col_name, col_info[:db_type], primary_key: col_info[:primary_key], allow_null: col_info[:allow_null]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# 4. Migrate data
|
|
112
|
+
[:users, :pages, :posts, :projects, :products].each do |table|
|
|
113
|
+
print " Migrating #{table}... "
|
|
114
|
+
rows = []
|
|
115
|
+
|
|
116
|
+
# Try fetching from Mongo if it exists
|
|
117
|
+
source_mongo = defined?(MONGO_CLIENT) ? MONGO_CLIENT : nil
|
|
118
|
+
if source_mongo
|
|
119
|
+
begin
|
|
120
|
+
mongo_rows = source_mongo[table].find.to_a
|
|
121
|
+
if mongo_rows.any?
|
|
122
|
+
rows = mongo_rows.map do |r|
|
|
123
|
+
r[:id] = r[:_id].to_s if r[:_id]
|
|
124
|
+
r.delete(:_id)
|
|
125
|
+
r
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
rescue
|
|
129
|
+
# Collection might not exist in Mongo, fallback to SQL
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Fallback to SQL if no rows found in Mongo
|
|
134
|
+
if rows.empty?
|
|
135
|
+
begin
|
|
136
|
+
rows = source_db[table].all
|
|
137
|
+
rescue
|
|
138
|
+
rows = []
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Clear target table before migrating to avoid duplicates
|
|
143
|
+
if target_db && target_db.table_exists?(table)
|
|
144
|
+
target_db[table].delete
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
count = 0
|
|
148
|
+
rows.each do |row|
|
|
149
|
+
if target_type == 'mongodb' && table == :users
|
|
150
|
+
# Users go to Mongo in Mongo mode
|
|
151
|
+
target_mongo[:users].update_one({ username: row[:username] }, { "$set" => row.reject{|k| k == :id} }, { upsert: true })
|
|
152
|
+
elsif target_db
|
|
153
|
+
# Deep cleaning and type conversion for SQLite compatibility
|
|
154
|
+
target_columns = target_db[table].columns
|
|
155
|
+
clean_row = {}
|
|
156
|
+
row.each do |k, v|
|
|
157
|
+
sym_k = k.to_sym
|
|
158
|
+
if target_columns.include?(sym_k) && sym_k != :id
|
|
159
|
+
if v.is_a?(BSON::ObjectId)
|
|
160
|
+
clean_row[sym_k] = v.to_s
|
|
161
|
+
elsif v.is_a?(Time)
|
|
162
|
+
clean_row[sym_k] = v.strftime('%Y-%m-%d %H:%M:%S')
|
|
163
|
+
else
|
|
164
|
+
clean_row[sym_k] = v
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
target_db[table].insert(clean_row)
|
|
169
|
+
end
|
|
170
|
+
count += 1
|
|
171
|
+
end
|
|
172
|
+
puts "#{count} rows."
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# 5. Update configuration to point to new DB
|
|
176
|
+
absolute_ofa_path = File.expand_path(__FILE__)
|
|
177
|
+
system("ruby #{absolute_ofa_path} db switch #{target_type} #{target_name}")
|
|
178
|
+
|
|
179
|
+
puts "✅ Migration successful!"
|
|
180
|
+
end
|
|
181
|
+
|
|
69
182
|
def ensure_initialized!
|
|
70
183
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
71
184
|
unless File.exist?(config_path)
|
|
@@ -113,7 +226,7 @@ when 'init'
|
|
|
113
226
|
puts " / __ \\/ ____/ / | "
|
|
114
227
|
puts " / / / / /_ / /| | Framework "
|
|
115
228
|
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
116
|
-
puts "\\____/_/ /_/ |_|
|
|
229
|
+
puts "\\____/_/ /_/ |_| v4.0.0 "
|
|
117
230
|
puts " "
|
|
118
231
|
puts "Initializing One-For-All project as '#{app_type}' in #{PROJECT_ROOT}..."
|
|
119
232
|
|
|
@@ -322,6 +435,14 @@ when 'init'
|
|
|
322
435
|
def index; end
|
|
323
436
|
end
|
|
324
437
|
RUBY
|
|
438
|
+
when 'e_commerce'
|
|
439
|
+
puts " Scaffolding E-Commerce starter..."
|
|
440
|
+
File.write(File.join(PROJECT_ROOT, 'app/controllers/products_controller.rb'), <<~RUBY)
|
|
441
|
+
require_relative 'application_controller'
|
|
442
|
+
class ProductsController < ApplicationController
|
|
443
|
+
def index; end
|
|
444
|
+
end
|
|
445
|
+
RUBY
|
|
325
446
|
end
|
|
326
447
|
|
|
327
448
|
puts "Done! Run 'ofa run' to start your app."
|
|
@@ -396,8 +517,8 @@ when 'feature'
|
|
|
396
517
|
when 'type'
|
|
397
518
|
ensure_initialized!
|
|
398
519
|
name = ARGV.shift
|
|
399
|
-
unless %w[landing_page portfolio blog].include?(name)
|
|
400
|
-
puts "❌ Error: Invalid app type '#{name}'. Choose: landing_page, portfolio, blog."
|
|
520
|
+
unless %w[landing_page portfolio blog e_commerce].include?(name)
|
|
521
|
+
puts "❌ Error: Invalid app type '#{name}'. Choose: landing_page, portfolio, blog, e_commerce."
|
|
401
522
|
exit 1
|
|
402
523
|
end
|
|
403
524
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
|
@@ -433,8 +554,10 @@ when 'storage'
|
|
|
433
554
|
puts "Application storage set to '#{name}'."
|
|
434
555
|
|
|
435
556
|
when 'reset-password'
|
|
557
|
+
ENV['SKIP_MODELS'] = '1'
|
|
436
558
|
Object.const_set(:APP_ROOT, PROJECT_ROOT) unless defined?(APP_ROOT)
|
|
437
559
|
require File.join(FRAMEWORK_ROOT, 'config', 'boot')
|
|
560
|
+
require File.join(FRAMEWORK_ROOT, 'app', 'models', 'user.rb')
|
|
438
561
|
user_name = ARGV.shift
|
|
439
562
|
new_pwd = ARGV.shift
|
|
440
563
|
if user_name.nil? || new_pwd.nil?
|
|
@@ -470,8 +593,16 @@ when 'db'
|
|
|
470
593
|
type = ARGV.shift
|
|
471
594
|
db_name = ARGV.shift
|
|
472
595
|
case type
|
|
473
|
-
when 'env'
|
|
596
|
+
when 'env', 'mongo', 'mongodb'
|
|
474
597
|
config = { "adapter" => "env" }
|
|
598
|
+
if db_name && db_name.start_with?('mongodb')
|
|
599
|
+
env_path = File.join(PROJECT_ROOT, '.env')
|
|
600
|
+
env_lines = File.exist?(env_path) ? File.readlines(env_path) : []
|
|
601
|
+
env_lines.reject! { |l| l.start_with?('DATABASE_URL=') }
|
|
602
|
+
env_lines << "DATABASE_URL=#{db_name}\n"
|
|
603
|
+
File.write(env_path, env_lines.join)
|
|
604
|
+
puts "✅ Connection string saved to .env"
|
|
605
|
+
end
|
|
475
606
|
when 'sqlite'
|
|
476
607
|
config = { "adapter" => "sqlite", "database" => db_name || "db/development.sqlite3" }
|
|
477
608
|
else
|
|
@@ -481,6 +612,14 @@ when 'db'
|
|
|
481
612
|
puts "Switched to #{type} mode."
|
|
482
613
|
when 'migrate'
|
|
483
614
|
run_migrations
|
|
615
|
+
when 'migrate-data'
|
|
616
|
+
target_type = ARGV.shift
|
|
617
|
+
target_name = ARGV.shift
|
|
618
|
+
if target_type.nil?
|
|
619
|
+
puts "Usage: ofa db migrate-data TYPE [NAME/URL]"
|
|
620
|
+
exit 1
|
|
621
|
+
end
|
|
622
|
+
perform_db_migration(target_type, target_name)
|
|
484
623
|
end
|
|
485
624
|
|
|
486
625
|
when 'run'
|
data/config/boot.rb
CHANGED
|
@@ -76,6 +76,7 @@ require_relative 'database'
|
|
|
76
76
|
# Autoloading (Framework Core first, then APP_ROOT)
|
|
77
77
|
framework_app = File.expand_path('../app', __dir__)
|
|
78
78
|
['controllers', 'models', 'middleware', 'helpers'].each do |folder|
|
|
79
|
+
next if folder == 'models' && ENV['SKIP_MODELS']
|
|
79
80
|
loaded = []
|
|
80
81
|
# Load framework core
|
|
81
82
|
Dir.glob(File.join(framework_app, folder, '*.rb')).each do |f|
|
data/config/database.json
CHANGED
data/config/database.rb
CHANGED
|
@@ -114,6 +114,22 @@ if DB
|
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
unless DB.table_exists?(:products)
|
|
118
|
+
DB.create_table :products do
|
|
119
|
+
primary_key :id
|
|
120
|
+
String :name, null: false
|
|
121
|
+
String :slug, null: false, unique: true
|
|
122
|
+
String :description, text: true
|
|
123
|
+
Float :price, default: 0.0
|
|
124
|
+
Integer :stock, default: 0
|
|
125
|
+
String :image_url
|
|
126
|
+
String :category
|
|
127
|
+
TrueClass :is_active, default: true
|
|
128
|
+
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
|
129
|
+
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
117
133
|
# Quick Migration for existing tables
|
|
118
134
|
if DB.table_exists?(:pages)
|
|
119
135
|
DB.alter_table(:pages) { add_column :is_active, TrueClass, default: true unless DB[:pages].columns.include?(:is_active) }
|
data/config/routes.rb
CHANGED
|
@@ -11,11 +11,42 @@ ROUTES = EksCent::Router.new do
|
|
|
11
11
|
when 'blog'
|
|
12
12
|
posts = Post.where(is_active: true).order(Sequel.desc(:created_at)).all
|
|
13
13
|
res.render 'blog_home', title: "Blog - One-For-All", posts: posts
|
|
14
|
+
when 'e_commerce'
|
|
15
|
+
ProductsController.new(req, res).index
|
|
14
16
|
else
|
|
15
17
|
res.render 'index', title: "One-For-All Framework"
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
# --- E-Commerce Routes ---
|
|
22
|
+
get '/products' do |req, res|
|
|
23
|
+
ProductsController.new(req, res).index
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
get '/products/:slug' do |req, res|
|
|
27
|
+
ProductsController.new(req, res).show
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
get '/cart' do |req, res|
|
|
31
|
+
CartController.new(req, res).index
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
post '/cart/add' do |req, res|
|
|
35
|
+
CartController.new(req, res).add
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
post '/cart/update' do |req, res|
|
|
39
|
+
CartController.new(req, res).update
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
post '/cart/remove' do |req, res|
|
|
43
|
+
CartController.new(req, res).remove
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
post '/cart/clear' do |req, res|
|
|
47
|
+
CartController.new(req, res).clear
|
|
48
|
+
end
|
|
49
|
+
|
|
19
50
|
get '/docs' do |req, res|
|
|
20
51
|
res.render 'docs', title: "Documentation - One-For-All"
|
|
21
52
|
end
|
|
@@ -33,6 +64,7 @@ ROUTES = EksCent::Router.new do
|
|
|
33
64
|
resources :pages, prefix: '/dashboard'
|
|
34
65
|
resources :posts, prefix: '/dashboard'
|
|
35
66
|
resources :projects, prefix: '/dashboard'
|
|
67
|
+
resources :products, prefix: '/dashboard'
|
|
36
68
|
|
|
37
69
|
# Auth Routes
|
|
38
70
|
get '/login' do |req, res|
|
data/db/data.sqlite3
CHANGED
|
Binary file
|
data/db/development.sqlite3
CHANGED
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
change do
|
|
3
|
+
create_table(:products) do
|
|
4
|
+
primary_key :id
|
|
5
|
+
String :name, null: false
|
|
6
|
+
String :slug, null: false, unique: true
|
|
7
|
+
String :description, text: true
|
|
8
|
+
Float :price, default: 0.0
|
|
9
|
+
Integer :stock, default: 0
|
|
10
|
+
String :image_url
|
|
11
|
+
String :category
|
|
12
|
+
TrueClass :is_active, default: true
|
|
13
|
+
DateTime :created_at
|
|
14
|
+
DateTime :updated_at
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/db/target.sqlite3
ADDED
|
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
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ishikawa Uta
|
|
@@ -188,22 +188,28 @@ files:
|
|
|
188
188
|
- app/controllers/api_controller.rb
|
|
189
189
|
- app/controllers/application_controller.rb
|
|
190
190
|
- app/controllers/auth_controller.rb
|
|
191
|
+
- app/controllers/cart_controller.rb
|
|
191
192
|
- app/controllers/dashboard_controller.rb
|
|
192
193
|
- app/controllers/pages_controller.rb
|
|
193
194
|
- app/controllers/posts_controller.rb
|
|
195
|
+
- app/controllers/products_controller.rb
|
|
194
196
|
- app/controllers/projects_controller.rb
|
|
195
197
|
- app/helpers/cloudinary_helper.rb
|
|
196
198
|
- app/middleware/auth_middleware.rb
|
|
197
199
|
- app/middleware/csrf_middleware.rb
|
|
198
200
|
- app/models/page.rb
|
|
199
201
|
- app/models/post.rb
|
|
202
|
+
- app/models/product.rb
|
|
200
203
|
- app/models/project.rb
|
|
201
204
|
- app/models/user.rb
|
|
202
205
|
- app/views/blog_home.erb
|
|
206
|
+
- app/views/cart_index.erb
|
|
203
207
|
- app/views/cms/pages_form.erb
|
|
204
208
|
- app/views/cms/pages_index.erb
|
|
205
209
|
- app/views/cms/posts_form.erb
|
|
206
210
|
- app/views/cms/posts_index.erb
|
|
211
|
+
- app/views/cms/products_form.erb
|
|
212
|
+
- app/views/cms/products_index.erb
|
|
207
213
|
- app/views/cms/projects_form.erb
|
|
208
214
|
- app/views/cms/projects_index.erb
|
|
209
215
|
- app/views/dashboard.erb
|
|
@@ -216,6 +222,8 @@ files:
|
|
|
216
222
|
- app/views/portfolio.erb
|
|
217
223
|
- app/views/post.erb
|
|
218
224
|
- app/views/posts/hello_world.erb
|
|
225
|
+
- app/views/product_show.erb
|
|
226
|
+
- app/views/products_index.erb
|
|
219
227
|
- app/views/project.erb
|
|
220
228
|
- bin/ofa
|
|
221
229
|
- config.eks
|
|
@@ -229,6 +237,8 @@ files:
|
|
|
229
237
|
- config/routes.rb
|
|
230
238
|
- db/data.sqlite3
|
|
231
239
|
- db/development.sqlite3
|
|
240
|
+
- db/migrations/20260502000000_create_products.rb
|
|
241
|
+
- db/target.sqlite3
|
|
232
242
|
- ofa
|
|
233
243
|
- public/css/cms.css
|
|
234
244
|
- public/images/logo.jpg
|