eksa-framework 3.3.2 → 3.4.3

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.
@@ -0,0 +1,83 @@
1
+ <div class="glass rounded-3xl p-10 text-white animate__animated animate__fadeIn">
2
+ <div class="flex items-center justify-between mb-10 pb-6 border-b border-white/10">
3
+ <div class="flex items-center gap-4">
4
+ <div class="p-4 bg-indigo-500 rounded-2xl shadow-xl">
5
+ <i data-lucide="layout-dashboard" class="w-8 h-8"></i>
6
+ </div>
7
+ <div>
8
+ <h1 class="text-4xl font-extrabold tracking-tight">CMS <span class="text-indigo-300">Dashboard</span></h1>
9
+ <p class="text-white/50">Kelola visibilitas postingan blog Anda</p>
10
+ </div>
11
+ </div>
12
+ <div class="flex items-center gap-4">
13
+ <span class="px-4 py-2 bg-white/5 rounded-xl border border-white/10 text-sm font-medium text-white/80">
14
+ <i data-lucide="user" class="w-4 h-4 inline-block mr-1"></i> Admin
15
+ </span>
16
+ <a href="/auth/logout" class="px-4 py-2 bg-red-500/20 hover:bg-red-500/40 text-red-300 rounded-xl border border-red-500/30 transition shadow-lg text-sm font-bold flex items-center gap-2">
17
+ <i data-lucide="log-out" class="w-4 h-4"></i> Logout
18
+ </a>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="overflow-hidden rounded-2xl border border-white/10 bg-black/20">
23
+ <table class="w-full text-left border-collapse">
24
+ <thead>
25
+ <tr class="bg-white/5 text-indigo-200 text-xs uppercase tracking-widest">
26
+ <th class="p-4 font-bold border-b border-white/10">Postingan</th>
27
+ <th class="p-4 font-bold border-b border-white/10">Kategori</th>
28
+ <th class="p-4 font-bold border-b border-white/10">Status</th>
29
+ <th class="p-4 font-bold border-b border-white/10 text-right">Aksi</th>
30
+ </tr>
31
+ </thead>
32
+ <tbody class="divide-y divide-white/5">
33
+ <% if @posts.empty? %>
34
+ <tr>
35
+ <td colspan="4" class="p-8 text-center text-white/40">
36
+ <i data-lucide="folder-open" class="w-8 h-8 mx-auto mb-2 opacity-50"></i>
37
+ Belum ada postingan markdown di _posts/
38
+ </td>
39
+ </tr>
40
+ <% else %>
41
+ <% @posts.each do |post| %>
42
+ <tr class="hover:bg-white/5 transition">
43
+ <td class="p-4">
44
+ <p class="font-bold text-lg"><%= post.title %></p>
45
+ <p class="text-xs text-white/40 font-mono"><%= post.filename %></p>
46
+ </td>
47
+ <td class="p-4">
48
+ <span class="text-[10px] font-bold text-indigo-300 uppercase tracking-widest px-2 py-1 bg-indigo-500/20 rounded-md border border-indigo-500/30">
49
+ <%= post.category %>
50
+ </span>
51
+ </td>
52
+ <td class="p-4">
53
+ <% if post.published? %>
54
+ <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-bold bg-emerald-500/20 text-emerald-300 border border-emerald-500/30">
55
+ <span class="w-2 h-2 rounded-full bg-emerald-400"></span> Aktif
56
+ </span>
57
+ <% else %>
58
+ <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-bold bg-gray-500/20 text-gray-400 border border-gray-500/30">
59
+ <span class="w-2 h-2 rounded-full bg-gray-400"></span> Draft
60
+ </span>
61
+ <% end %>
62
+ </td>
63
+ <td class="p-4 text-right space-x-2">
64
+ <a href="/cms/edit/<%= post.slug %>" class="inline-flex items-center justify-center p-2 rounded-lg bg-yellow-500/20 hover:bg-yellow-500/40 text-yellow-300 transition border border-yellow-500/30" title="Edit Post">
65
+ <i data-lucide="edit" class="w-4 h-4"></i>
66
+ </a>
67
+ <a href="/posts/<%= post.slug %>" target="_blank" class="inline-flex items-center justify-center p-2 rounded-lg bg-white/5 hover:bg-white/10 text-white/70 transition border border-white/10" title="Lihat Post">
68
+ <i data-lucide="external-link" class="w-4 h-4"></i>
69
+ </a>
70
+ <a href="/cms/toggle/<%= post.slug %>" class="inline-flex items-center justify-center p-2 rounded-lg bg-indigo-500/20 hover:bg-indigo-500/40 text-indigo-300 transition border border-indigo-500/30" title="Toggle Status">
71
+ <i data-lucide="<%= post.published? ? 'eye-off' : 'eye' %>" class="w-4 h-4"></i>
72
+ </a>
73
+ <a href="/cms/delete/<%= post.slug %>" onclick="return confirm('Yakin ingin menghapus postingan ini selamanya?')" class="inline-flex items-center justify-center p-2 rounded-lg bg-red-500/20 hover:bg-red-500/40 text-red-300 transition border border-red-500/30" title="Hapus Permanen">
74
+ <i data-lucide="trash-2" class="w-4 h-4"></i>
75
+ </a>
76
+ </td>
77
+ </tr>
78
+ <% end %>
79
+ <% end %>
80
+ </tbody>
81
+ </table>
82
+ </div>
83
+ </div>
data/lib/eksa.rb CHANGED
@@ -1,19 +1,25 @@
1
1
  require 'rack'
2
+ require 'rack/session'
2
3
  require 'json'
3
4
  require_relative 'eksa/version'
4
5
  require_relative 'eksa/controller'
5
6
  require_relative 'eksa/model'
6
7
  require_relative 'eksa/markdown_post'
8
+ require_relative 'eksa/user'
9
+ require_relative 'eksa/auth_controller'
10
+ require_relative 'eksa/cms_controller'
7
11
 
8
12
  module Eksa
9
13
  class Application
10
- attr_reader :config, :middlewares
14
+ attr_reader :config, :middlewares, :features
11
15
 
12
16
  def initialize
13
17
  @routes = {}
14
18
  @middlewares = []
19
+ @features = load_feature_flags
15
20
  @config = {
16
- db_path: File.expand_path("./db/eksa_app.db")
21
+ db_path: File.expand_path("./db/eksa_app.db"),
22
+ session_secret: ENV['SESSION_SECRET'] || 'eksa_super_secret_key_change_me_in_production_make_it_sixty_four_bytes_or_more'
17
23
  }
18
24
  yield self if block_given?
19
25
  configure_framework
@@ -21,6 +27,40 @@ module Eksa
21
27
 
22
28
  def configure_framework
23
29
  Eksa::Model.database_path = @config[:db_path]
30
+
31
+ # Setup Session Middleware for Authentication
32
+ use Rack::Session::Cookie, secret: @config[:session_secret], key: 'eksa.session'
33
+
34
+ auto_mount_features
35
+ end
36
+
37
+ def load_feature_flags
38
+ config_path = File.expand_path('./.eksa.json')
39
+ if File.exist?(config_path)
40
+ JSON.parse(File.read(config_path))
41
+ else
42
+ { 'cms' => false, 'auth' => false }
43
+ end
44
+ rescue JSON::ParserError
45
+ { 'cms' => false, 'auth' => false }
46
+ end
47
+
48
+ def auto_mount_features
49
+ if @features['auth']
50
+ add_route "/auth/login", Eksa::AuthController, :login
51
+ add_route "/auth/register", Eksa::AuthController, :register
52
+ add_route "/auth/logout", Eksa::AuthController, :logout
53
+ add_route "/auth/process_login", Eksa::AuthController, :process_login
54
+ add_route "/auth/process_register", Eksa::AuthController, :process_register
55
+ end
56
+
57
+ if @features['cms']
58
+ add_route "/cms", Eksa::CmsController, :index
59
+ add_route "/cms/edit/:slug", Eksa::CmsController, :edit
60
+ add_route "/cms/update/:slug", Eksa::CmsController, :update_post
61
+ add_route "/cms/toggle/:slug", Eksa::CmsController, :toggle_status
62
+ add_route "/cms/delete/:slug", Eksa::CmsController, :delete_post
63
+ end
24
64
  end
25
65
 
26
66
  def add_route(path, controller_class, action)
@@ -82,6 +122,9 @@ module Eksa
82
122
  else
83
123
  response = Rack::Response.new
84
124
  if controller_instance.status == 302
125
+ if controller_instance.flash[:notice] && !controller_instance.flash[:notice].empty?
126
+ response.set_cookie('eksa_flash', value: controller_instance.flash[:notice], path: '/')
127
+ end
85
128
  response.redirect(controller_instance.redirect_url, 302)
86
129
  else
87
130
  response.write(response_data)
@@ -89,7 +132,9 @@ module Eksa
89
132
  end
90
133
  end
91
134
 
92
- response.delete_cookie('eksa_flash') if flash_message
135
+ if flash_message && controller_instance.status != 302
136
+ response.delete_cookie('eksa_flash', path: '/')
137
+ end
93
138
  response.finish
94
139
  else
95
140
  html = <<~HTML
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eksa-framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.2
4
+ version: 3.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - IshikawaUta
@@ -93,6 +93,34 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '1.1'
96
+ - !ruby/object:Gem::Dependency
97
+ name: bcrypt
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.1'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.1'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rack-session
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '2.0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '2.0'
96
124
  description: Framework MVC ringan dengan tema modern, sistem routing, dan auto-database
97
125
  SQLite.
98
126
  email:
@@ -102,6 +130,7 @@ executables:
102
130
  extensions: []
103
131
  extra_rdoc_files: []
104
132
  files:
133
+ - ".eksa.json"
105
134
  - Gemfile
106
135
  - Gemfile.lock
107
136
  - LICENSE
@@ -123,12 +152,20 @@ files:
123
152
  - db/setup.rb
124
153
  - exe/eksa
125
154
  - lib/eksa.rb
155
+ - lib/eksa/auth_controller.rb
156
+ - lib/eksa/cms_controller.rb
126
157
  - lib/eksa/controller.rb
127
158
  - lib/eksa/markdown_post.rb
128
159
  - lib/eksa/model.rb
160
+ - lib/eksa/user.rb
129
161
  - lib/eksa/version.rb
162
+ - lib/eksa/views/auth/login.html.erb
163
+ - lib/eksa/views/auth/register.html.erb
164
+ - lib/eksa/views/cms/edit.html.erb
165
+ - lib/eksa/views/cms/index.html.erb
130
166
  - public/css/style.css
131
167
  - public/img/favicon.ico
168
+ - public/img/logo.jpg
132
169
  - public/img/logo.png
133
170
  - spec/application_spec.rb
134
171
  - spec/markdown_post_spec.rb