-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.rb
More file actions
193 lines (158 loc) · 5.25 KB
/
Copy pathapp.rb
File metadata and controls
193 lines (158 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# frozen_string_literal: true
require 'sinatra'
require 'sinatra/json'
require 'sqlite3'
require 'json'
require 'securerandom'
require_relative 'lib/database'
# ─── Configuration ────────────────────────────────────────────
set :port, ENV.fetch('PORT', 4567).to_i
set :bind, '0.0.0.0'
set :erb, escape_html: true
set :public_folder, File.join(__dir__, 'public')
set :views, File.join(__dir__, 'views')
enable :sessions
set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(32) }
configure do
Database.setup
end
# ─── Helpers ──────────────────────────────────────────────────
helpers do
def flash(type, message)
session[:flash] = { type: type, message: message }
end
def consume_flash
msg = session.delete(:flash)
msg
end
def h(text)
Rack::Utils.escape_html(text.to_s)
end
def valid_email?(email)
email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
end
def valid_name?(name)
name.is_a?(String) && name.strip.length.between?(1, 100)
end
end
# ─── Web Routes ───────────────────────────────────────────────
# Dashboard — list users with search & pagination
get '/' do
page = [params.fetch('page', 1).to_i, 1].max
per = 10
search = params['q'].to_s.strip
offset = (page - 1) * per
if search.empty?
@users = Database.all_users(limit: per, offset: offset)
@total = Database.user_count
else
@users = Database.search_users(search, limit: per, offset: offset)
@total = Database.search_count(search)
end
@page = page
@per = per
@search = search
@total_pages = [(@total.to_f / per).ceil, 1].max
@stats = Database.stats
erb :index
end
# New user form
get '/users/new' do
@user = { name: '', email: '', role: 'member' }
erb :form
end
# Create user
post '/users' do
name = params['name'].to_s.strip
email = params['email'].to_s.strip
role = %w[admin member viewer].include?(params['role']) ? params['role'] : 'member'
unless valid_name?(name)
flash(:error, 'Name is required (1–100 characters).')
@user = { name: name, email: email, role: role }
return erb(:form)
end
unless valid_email?(email)
flash(:error, 'Please enter a valid email address.')
@user = { name: name, email: email, role: role }
return erb(:form)
end
if Database.email_exists?(email)
flash(:error, 'A user with that email already exists.')
@user = { name: name, email: email, role: role }
return erb(:form)
end
Database.create_user(name, email, role)
flash(:success, "User \"#{name}\" created successfully.")
redirect '/'
end
# Edit user form
get '/users/:id/edit' do
@user = Database.find_user(params['id'].to_i)
halt 404, erb(:not_found) unless @user
erb :form
end
# Update user
post '/users/:id' do
id = params['id'].to_i
@user = Database.find_user(id)
halt 404, erb(:not_found) unless @user
name = params['name'].to_s.strip
email = params['email'].to_s.strip
role = %w[admin member viewer].include?(params['role']) ? params['role'] : @user['role']
unless valid_name?(name)
flash(:error, 'Name is required (1–100 characters).')
@user = { 'id' => id, 'name' => name, 'email' => email, 'role' => role }
return erb(:form)
end
unless valid_email?(email)
flash(:error, 'Please enter a valid email address.')
@user = { 'id' => id, 'name' => name, 'email' => email, 'role' => role }
return erb(:form)
end
if Database.email_exists?(email, exclude_id: id)
flash(:error, 'A user with that email already exists.')
@user = { 'id' => id, 'name' => name, 'email' => email, 'role' => role }
return erb(:form)
end
Database.update_user(id, name, email, role)
flash(:success, "User \"#{name}\" updated successfully.")
redirect '/'
end
# Delete user
post '/users/:id/delete' do
id = params['id'].to_i
user = Database.find_user(id)
halt 404, erb(:not_found) unless user
Database.delete_user(id)
flash(:success, "User \"#{user['name']}\" deleted.")
redirect '/'
end
# Contact form submission
post '/contact' do
name = params['contact_name'].to_s.strip
email = params['contact_email'].to_s.strip
message = params['contact_message'].to_s.strip
if name.empty? || !valid_email?(email) || message.empty?
flash(:error, 'Please fill in all contact fields with valid data.')
redirect '/'
end
Database.create_message(name, email, message)
flash(:success, 'Message sent! We\'ll get back to you soon.')
redirect '/'
end
# ─── API Routes (JSON) ───────────────────────────────────────
get '/api/users' do
json Database.all_users(limit: 100, offset: 0)
end
get '/api/users/:id' do
user = Database.find_user(params['id'].to_i)
halt 404, json({ error: 'not found' }) unless user
json user
end
get '/api/stats' do
json Database.stats
end
# ─── Error Pages ──────────────────────────────────────────────
not_found do
erb :not_found
end