waxx 0.1.2
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/LICENSE +201 -0
- data/README.md +879 -0
- data/bin/waxx +120 -0
- data/lib/waxx/app.rb +173 -0
- data/lib/waxx/conf.rb +54 -0
- data/lib/waxx/console.rb +204 -0
- data/lib/waxx/csrf.rb +14 -0
- data/lib/waxx/database.rb +80 -0
- data/lib/waxx/encrypt.rb +38 -0
- data/lib/waxx/error.rb +60 -0
- data/lib/waxx/html.rb +33 -0
- data/lib/waxx/http.rb +268 -0
- data/lib/waxx/init.rb +273 -0
- data/lib/waxx/irb.rb +44 -0
- data/lib/waxx/irb_env.rb +18 -0
- data/lib/waxx/json.rb +23 -0
- data/lib/waxx/mongodb.rb +221 -0
- data/lib/waxx/mysql2.rb +234 -0
- data/lib/waxx/object.rb +115 -0
- data/lib/waxx/patch.rb +138 -0
- data/lib/waxx/pdf.rb +69 -0
- data/lib/waxx/pg.rb +246 -0
- data/lib/waxx/process.rb +270 -0
- data/lib/waxx/req.rb +116 -0
- data/lib/waxx/res.rb +98 -0
- data/lib/waxx/server.rb +304 -0
- data/lib/waxx/sqlite3.rb +237 -0
- data/lib/waxx/supervisor.rb +47 -0
- data/lib/waxx/test.rb +162 -0
- data/lib/waxx/util.rb +57 -0
- data/lib/waxx/version.rb +3 -0
- data/lib/waxx/view.rb +389 -0
- data/lib/waxx/waxx.rb +73 -0
- data/lib/waxx/x.rb +103 -0
- data/lib/waxx.rb +50 -0
- data/skel/README.md +11 -0
- data/skel/app/app/app.rb +39 -0
- data/skel/app/app/error/app_error.rb +16 -0
- data/skel/app/app/error/dhtml.rb +9 -0
- data/skel/app/app/error/html.rb +8 -0
- data/skel/app/app/error/json.rb +8 -0
- data/skel/app/app/error/pdf.rb +13 -0
- data/skel/app/app/log/app_log.rb +13 -0
- data/skel/app/app.rb +20 -0
- data/skel/app/home/home.rb +16 -0
- data/skel/app/home/html.rb +145 -0
- data/skel/app/html.rb +192 -0
- data/skel/app/usr/email.rb +66 -0
- data/skel/app/usr/html.rb +115 -0
- data/skel/app/usr/list.rb +51 -0
- data/skel/app/usr/password.rb +54 -0
- data/skel/app/usr/record.rb +98 -0
- data/skel/app/usr/usr.js +67 -0
- data/skel/app/usr/usr.rb +277 -0
- data/skel/app/waxx/waxx.rb +109 -0
- data/skel/bin/README.md +1 -0
- data/skel/db/README.md +11 -0
- data/skel/db/app/0-init.sql +88 -0
- data/skel/lib/README.md +1 -0
- data/skel/log/README.md +1 -0
- data/skel/opt/dev/config.yaml +1 -0
- data/skel/opt/prod/config.yaml +1 -0
- data/skel/opt/stage/config.yaml +1 -0
- data/skel/opt/test/config.yaml +1 -0
- data/skel/private/README.md +1 -0
- data/skel/public/lib/site.css +202 -0
- data/skel/public/lib/waxx/w.ico +0 -0
- data/skel/public/lib/waxx/w.png +0 -0
- data/skel/public/lib/waxx/waxx.js +111 -0
- data/skel/tmp/pids/README.md +1 -0
- data.tar.gz.sig +0 -0
- metadata +140 -0
- metadata.gz.sig +3 -0
data/skel/app/usr/usr.rb
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
module App::Usr
|
|
2
|
+
extend Waxx::Pg
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
has(
|
|
6
|
+
id: {type: "integer",label:"ID"},
|
|
7
|
+
usr_name: {type: "character",label:""},
|
|
8
|
+
password_sha256: {type: "character",label:""},
|
|
9
|
+
salt_aes256: {type: "character",label:""},
|
|
10
|
+
failed_login_count: {type: "smallint",label:""},
|
|
11
|
+
require_new_password: {type: "smallint",label:""},
|
|
12
|
+
password_mod_date: {type: "date",label:""},
|
|
13
|
+
last_login_host: {type: "character",label:""},
|
|
14
|
+
last_login_date: {type: "timestamp",label:""},
|
|
15
|
+
create_date: {type: "timestamp",label:""},
|
|
16
|
+
mod_date: {type: "timestamp",label:""},
|
|
17
|
+
create_by_id: {type: "integer",label:""},
|
|
18
|
+
mod_by_id: {type: "integer",label:""},
|
|
19
|
+
key: {type: "character",label:""},
|
|
20
|
+
key_sent_date: {type: "timestamp",label:""}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Create the first usr (or any usr). Run from the console: App::Usr.init(x)
|
|
24
|
+
def init(x)
|
|
25
|
+
data = {}
|
|
26
|
+
puts "Create a new user"
|
|
27
|
+
print "Email: "
|
|
28
|
+
data[:usr_name] = gets.chomp
|
|
29
|
+
print "Password: "
|
|
30
|
+
data[:password] = gets.chomp
|
|
31
|
+
salt, encrypted_password = salt_password(x, data/:password)
|
|
32
|
+
data[:salt_aes256] = salt
|
|
33
|
+
data[:password_sha256] = encrypted_password
|
|
34
|
+
u = post(x, data, returning: 'id')
|
|
35
|
+
puts "User #{data/:usr_name} added (usr.id: #{u['id']})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def login(x, usr_name, password)
|
|
39
|
+
u = usr(x, usr_name:usr_name)
|
|
40
|
+
return false, "Invalid user name", {} if u.nil? or u['id'].nil?
|
|
41
|
+
pass = password?(u, password, u['salt_aes256'])
|
|
42
|
+
return false, "Invalid password", {} if pass == false
|
|
43
|
+
login_successful(x, u)
|
|
44
|
+
return true, "Login successful", u
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def record(x, id:0)
|
|
48
|
+
get_by_id(x, id, "id, usr_name, password_sha256, last_login_date, last_login_host, password_mod_date")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def usr(x, usr_name:'', id:0)
|
|
52
|
+
x.db.exec("
|
|
53
|
+
SELECT id, company_id, usr_name, password_sha256, salt_aes256, last_login_date, last_login_host
|
|
54
|
+
FROM usr
|
|
55
|
+
WHERE usr_name = $1
|
|
56
|
+
OR usr.id = $2
|
|
57
|
+
LIMIT 1", [usr_name, id]).first #rescue {}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def password?(u, password, salt)
|
|
61
|
+
#u['password_sha256'] == Digest::SHA256.hexdigest(App.decrypt(salt) + password)
|
|
62
|
+
u['password_sha256'] == Digest::SHA256.hexdigest(Conf['encryption']['old_key'] + password)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def salt_password(x, password)
|
|
66
|
+
salt = Waxx.random_string(32, :any)
|
|
67
|
+
[App.encrypt(salt), Digest::SHA256.hexdigest(salt + password)]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def set_password(x, id, password)
|
|
71
|
+
salt, epw = salt_password(x, password)
|
|
72
|
+
put(x, id, {salt_aes256: salt, password_sha256: epw})
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def create(x, data={}, returning: "id")
|
|
76
|
+
data = blk.call if block_given?
|
|
77
|
+
salt, encrypted_password = salt_password(x, data/:password)
|
|
78
|
+
data[:salt_aes256] = salt
|
|
79
|
+
data[:password_sha256] = encrypted_password
|
|
80
|
+
post(x, data, returning: returning)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def login_successful(x, usr)
|
|
84
|
+
Waxx.debug "login_successful"
|
|
85
|
+
x.db.exec("
|
|
86
|
+
update usr set
|
|
87
|
+
last_login_date = now(),
|
|
88
|
+
last_login_host = $1,
|
|
89
|
+
failed_login_count = 0
|
|
90
|
+
where usr_name = $2",
|
|
91
|
+
[
|
|
92
|
+
x.req.env['X-Forwarded-For'],
|
|
93
|
+
usr['usr_name']
|
|
94
|
+
]
|
|
95
|
+
)
|
|
96
|
+
set_cookies(x, usr)
|
|
97
|
+
debug x.ua.inspect
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def key_for_reset(x, id)
|
|
101
|
+
x.db.exec("
|
|
102
|
+
update usr set
|
|
103
|
+
key = generate_key(),
|
|
104
|
+
key_sent_date = now()
|
|
105
|
+
where id = $1
|
|
106
|
+
returning key",
|
|
107
|
+
[ id ]
|
|
108
|
+
).first['key']
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def set_cookies(x, usr)
|
|
112
|
+
x.usr['id'] = usr['id']
|
|
113
|
+
x.usr['cid'] = usr['company_id']
|
|
114
|
+
x.usr['grp'] = groups(x, usr['id']).push("user")
|
|
115
|
+
x.usr['uk'] = Waxx.random_string(20) # The Update Key is used to protect against CSRF
|
|
116
|
+
x.usr['la'] = Time.now.to_i # Last Activity used for session expiration
|
|
117
|
+
x.ua['id'] = usr['id']
|
|
118
|
+
x.ua['cid'] = usr['company_id']
|
|
119
|
+
x.ua['un'] = usr['usr_name']
|
|
120
|
+
x.ua['rm'] = x['remember_me'].to_i # Remember the user name for the login or UI features
|
|
121
|
+
x.ua['ll'] = Time.now.to_i # Last Login used for session expiration and welcome back
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def groups(x, usr_id)
|
|
125
|
+
x.db.exec("
|
|
126
|
+
SELECT name
|
|
127
|
+
FROM grp JOIN usr_grp ON grp.id = usr_grp.grp_id
|
|
128
|
+
WHERE usr_grp.usr_id = $1",[usr_id]).column_values(0)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
runs(
|
|
132
|
+
default: "list",
|
|
133
|
+
home: {
|
|
134
|
+
desc: "The home page of a logged in usr",
|
|
135
|
+
acl: "user",
|
|
136
|
+
get: proc{|x, *args|
|
|
137
|
+
usr = App::Usr.record(x, id: x.usr['id'])
|
|
138
|
+
person = App::Person.record(x, id: x.usr['id']).first
|
|
139
|
+
App::Html.render(x,
|
|
140
|
+
title: "#{person['first_name'].h} #{person['last_name'].h}",
|
|
141
|
+
content: App::Usr::Html.home(x, usr: usr, person: person)
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
list: {
|
|
146
|
+
desc: "List users",
|
|
147
|
+
acl: %w(admin),
|
|
148
|
+
get: lambda{|x| List.run(x) }
|
|
149
|
+
},
|
|
150
|
+
record: {
|
|
151
|
+
desc: "Edit a usr record",
|
|
152
|
+
acl: %w(admin),
|
|
153
|
+
get: lambda{|x, id, *args| Record.run(x, id:id) },
|
|
154
|
+
post: lambda{|x, id, *args| Record.save(x, id:id, data:x.req.post) }
|
|
155
|
+
},
|
|
156
|
+
login: {
|
|
157
|
+
desc: "Login",
|
|
158
|
+
get: proc{|x, *args|
|
|
159
|
+
#App::Html.page(x, title: "Login to #{Conf['site']['name']}", content: App::Usr::Html.login(x))
|
|
160
|
+
x.res.redirect "/app/login?return_to=#{Waxx::Html.qs x.req.uri}"
|
|
161
|
+
},
|
|
162
|
+
post: proc{|x, *args|
|
|
163
|
+
success, message, u = login(x, x['usr_name'], x['password'])
|
|
164
|
+
if success
|
|
165
|
+
if x.ext == 'json'
|
|
166
|
+
x.res['Content-Type'] = "application/json"
|
|
167
|
+
x << { success: success, message: message, usr: usr, key: key }.to_json
|
|
168
|
+
else
|
|
169
|
+
if x['return_to'].to_s[0] == '/'
|
|
170
|
+
x << %(<html><script>location = '#{x['return_to']}';</script></html>)
|
|
171
|
+
else
|
|
172
|
+
x << %(<html>Error: Must return to a local path. Attempted return_to: #{x['return_to'].to_s.h}</html>)
|
|
173
|
+
end
|
|
174
|
+
# Some browsers not storing cookies on redirect
|
|
175
|
+
#x.res.status = 302
|
|
176
|
+
#x.res['Location'] = x['return_to']
|
|
177
|
+
end
|
|
178
|
+
else
|
|
179
|
+
if x.ext == 'json'
|
|
180
|
+
x.res['Content-Type'] = "application/json"
|
|
181
|
+
x << { success: success, message: message, usr: usr, key: key }.to_json
|
|
182
|
+
else
|
|
183
|
+
App::Html.page(x, title: "Login Error", message:{type:"danger", message: message}, content: App::Usr::Html.login(x, return_to:x['return_to']))
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
logout: {
|
|
189
|
+
desc: "Logout of the app",
|
|
190
|
+
get: proc{|x|
|
|
191
|
+
x.usr['id'] = nil
|
|
192
|
+
x.usr['grp'] = nil
|
|
193
|
+
if x.ext == "json"
|
|
194
|
+
x << %({"success":true})
|
|
195
|
+
else
|
|
196
|
+
x.res.status = 302
|
|
197
|
+
x.res['Location'] = '/'
|
|
198
|
+
end
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
password_reset:{
|
|
202
|
+
desc: "Send the user a password reset link.",
|
|
203
|
+
post: proc{|x, *args|
|
|
204
|
+
# See if the user exists
|
|
205
|
+
u = App::Usr.usr(x, usr_name: x['email'])
|
|
206
|
+
if u.nil?
|
|
207
|
+
# Send an email that we do not have an account for that user
|
|
208
|
+
App::Email.post(x, Email.email_not_found(x, x['email']))
|
|
209
|
+
else
|
|
210
|
+
# Send the password reset email
|
|
211
|
+
k = App::Usr.key_for_reset(x, u['id'])
|
|
212
|
+
App::Email.post(x, Email.password_reset(x, u, k))
|
|
213
|
+
end
|
|
214
|
+
App::Html.page(x, title:"Password Reset Sent", content:"A link to reset your password has been sent to #{x['email'].h}. Please check your email. (It may be in your Spam or Junk folder.)")
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
password: {
|
|
218
|
+
desc: "Form to select a new password",
|
|
219
|
+
get: lambda{|x, id=nil, key=nil, *args|
|
|
220
|
+
if id.nil? and not x.usr?
|
|
221
|
+
return App.login_needed(x)
|
|
222
|
+
end
|
|
223
|
+
if key.nil? and not x.usr?
|
|
224
|
+
return App.login_needed(x)
|
|
225
|
+
end
|
|
226
|
+
if x.usr?
|
|
227
|
+
App::Html.render(x, title: "Reset Password", content:Html.change_password(x))
|
|
228
|
+
else
|
|
229
|
+
u = App::Usr.get_by_id(x, id, "usr_name, key, key_sent_date")
|
|
230
|
+
if u['key'] != key
|
|
231
|
+
return App.error(x, title:"Link Invalid",
|
|
232
|
+
message:"The link you are using does not match our records.
|
|
233
|
+
If you requested a password reset multiple times today,
|
|
234
|
+
please use the most recent link that you received. If that does
|
|
235
|
+
not work, please request another link on the login page.")
|
|
236
|
+
end
|
|
237
|
+
if u['key_sent_date'] < Time.new - 21600 # 6 hours
|
|
238
|
+
return App.error(x, title:"Link Expired",
|
|
239
|
+
message:"The link you are using expired after 6 hours to protect
|
|
240
|
+
your account. Please request another link on the login page.")
|
|
241
|
+
end
|
|
242
|
+
App::Html.page(x, title: "Reset Password", content:Html.change_password(x, u, key))
|
|
243
|
+
end
|
|
244
|
+
},
|
|
245
|
+
post: lambda{|x, id=nil, key=nil, *args|
|
|
246
|
+
if x['password1'] != x['password2']
|
|
247
|
+
return App::Html.render(x, title: "Reset Password", content:Html.password_form(x), message:{type:"danger", message:"Your passwords do not match. Please try again."})
|
|
248
|
+
end
|
|
249
|
+
if x['password1'] =~ /[a-z]|[A-Z]/
|
|
250
|
+
return App::Html.render(x, title: "Reset Password", content:Html.password_form(x), message:{type:"danger", message:"Your passwords do not match. Please try again."})
|
|
251
|
+
end
|
|
252
|
+
if id.nil? and x.usr?
|
|
253
|
+
App::Usr.set_password(x, x.usr['id'], x["password1"])
|
|
254
|
+
App::Html.page(x, title:"Password Reset Successful", content:"Your password has been reset.")
|
|
255
|
+
elsif id and key
|
|
256
|
+
u = App::Usr.get_by_id(x, id, "usr_name, key, key_sent_date")
|
|
257
|
+
if u['key'] != key
|
|
258
|
+
return App.error(x, title:"Link Invalid",
|
|
259
|
+
message:"The link you are using does not match our records.
|
|
260
|
+
If you requested a password reset multiple times today,
|
|
261
|
+
please use the most recent link that you received. If that does
|
|
262
|
+
not work, please request another link on the login page.")
|
|
263
|
+
end
|
|
264
|
+
App::Usr.set_password(x, id, x["password1"])
|
|
265
|
+
success, message, u = App::Usr.login(x, u['usr_name'], x['password1'])
|
|
266
|
+
App::Html.page(x, title:"Password Reset Successful", content:"Your password has been reset and you have been logged in.")
|
|
267
|
+
end
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
require_relative 'html'
|
|
274
|
+
require_relative 'email'
|
|
275
|
+
require_relative 'password'
|
|
276
|
+
require_relative 'record'
|
|
277
|
+
require_relative 'list'
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module App::Waxx
|
|
2
|
+
extend Waxx::Object
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
runs(
|
|
6
|
+
ok: {
|
|
7
|
+
desc: "Ping the app database to see if it is ok",
|
|
8
|
+
get: -> (x) {
|
|
9
|
+
x << (x.db.app.exec("select 1+1 as two").first['two'] == 2)
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
sleep: {
|
|
13
|
+
desc: "Sleep for seconds in args",
|
|
14
|
+
acl: %w(dev),
|
|
15
|
+
run: -> (x, secs=1) {
|
|
16
|
+
sleep(secs.to_i)
|
|
17
|
+
x.res.as :txt
|
|
18
|
+
x << "Done sleeping for #{secs.to_i} seconds"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
error: {
|
|
22
|
+
desc: "Raise an Error (to see what the error looks like and send email if configured)",
|
|
23
|
+
acl: %w(dev),
|
|
24
|
+
run: ->(x, *args){
|
|
25
|
+
raise "test error"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
env: {
|
|
29
|
+
desc: "Output all the input variables",
|
|
30
|
+
acl: "dev",
|
|
31
|
+
run: ->(x, *args) {
|
|
32
|
+
x.res.as :txt
|
|
33
|
+
x << {"x" => {
|
|
34
|
+
"meth" => x.meth,
|
|
35
|
+
"app" => x.app,
|
|
36
|
+
"act" => x.act,
|
|
37
|
+
"oid" => x.oid,
|
|
38
|
+
"args" => x.args,
|
|
39
|
+
"ext" => x.ext,
|
|
40
|
+
"usr" => x.usr,
|
|
41
|
+
"ua" => x.ua,
|
|
42
|
+
"req" => {
|
|
43
|
+
"meth" => x.req.meth,
|
|
44
|
+
"get" => x.req.get,
|
|
45
|
+
"post" => x.req.post,
|
|
46
|
+
"env" => x.req.env,
|
|
47
|
+
"cookies" => x.req.cookies,
|
|
48
|
+
"data" => x.req.data,
|
|
49
|
+
}
|
|
50
|
+
}}.to_yaml
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
raw: {
|
|
54
|
+
desc: "Output all the input variables",
|
|
55
|
+
run: -> (x, *args) {
|
|
56
|
+
x.res.as :txt
|
|
57
|
+
x << x.req.env.map{|n,v| "#{n}: #{v}"}.join("\r\n")
|
|
58
|
+
x << "\r\n\r\n"
|
|
59
|
+
x << x.req.data
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
desc: {
|
|
63
|
+
desc: "Describes all of the applications interfaces.",
|
|
64
|
+
acl: "dev",
|
|
65
|
+
get: ->(x, app="all"){
|
|
66
|
+
return App.error(x, status: 300, title: 'Request Error', message: 'This method only return json or yaml') unless %w(json yaml).include? x.ext
|
|
67
|
+
re = {}
|
|
68
|
+
describe = -> (ap) {
|
|
69
|
+
re[ap.to_s] = {}
|
|
70
|
+
return nil if App[ap].nil?
|
|
71
|
+
App[ap].each{|act,props|
|
|
72
|
+
re[ap.to_s][act.to_s] = {}
|
|
73
|
+
if props.respond_to?('each')
|
|
74
|
+
props.each{|n, v|
|
|
75
|
+
if Proc === v
|
|
76
|
+
re[ap.to_s][act.to_s][n.to_s] = Hash[v.parameters.map{|param| [param[1].to_s, param[0].to_s]}]
|
|
77
|
+
else
|
|
78
|
+
re[ap.to_s][act.to_s][n.to_s] = v
|
|
79
|
+
end
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
re[ap.to_s][act.to_s] = props
|
|
83
|
+
end
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if app == "all"
|
|
87
|
+
App.runs.keys.each{|k|
|
|
88
|
+
describe[k]
|
|
89
|
+
}
|
|
90
|
+
else
|
|
91
|
+
describe[app]
|
|
92
|
+
end
|
|
93
|
+
x << re.send("to_#{x.ext}")
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
threads: {
|
|
97
|
+
desc: "Show the status of all threads",
|
|
98
|
+
acl: "dev",
|
|
99
|
+
get: -> (x) {
|
|
100
|
+
x.res.as :txt
|
|
101
|
+
Thread.list.each{|t|
|
|
102
|
+
next if t[:name] == "main"
|
|
103
|
+
x << "#{t[:name]}: #{t[:status]}\n"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
end
|
data/skel/bin/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Put executable files like deploy scripts or maintenance scripts here.
|
data/skel/db/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
waxx migration files are stored in a folderf or each database.
|
|
2
|
+
|
|
3
|
+
To generate a migration file, run:
|
|
4
|
+
|
|
5
|
+
`waxx migration app migration-name`
|
|
6
|
+
|
|
7
|
+
replace `app` with the database name defined in config.yaml
|
|
8
|
+
|
|
9
|
+
To migrate the database run:
|
|
10
|
+
|
|
11
|
+
`waxx migrate` for all databases of `waxx migrate app` for only the app database. Replace `app` with the name of any defined database connection.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
BEGIN;
|
|
2
|
+
CREATE FUNCTION generate_key() RETURNS character varying
|
|
3
|
+
LANGUAGE sql IMMUTABLE
|
|
4
|
+
AS $$select md5(now()::varchar||random()::varchar||random()::varchar);$$;
|
|
5
|
+
CREATE TABLE app_log (
|
|
6
|
+
id serial primary key,
|
|
7
|
+
date_time timestamp without time zone DEFAULT now(),
|
|
8
|
+
usr_id integer,
|
|
9
|
+
category character varying(32),
|
|
10
|
+
name character varying(64),
|
|
11
|
+
value character varying(254),
|
|
12
|
+
related_id integer,
|
|
13
|
+
ip_address character varying(39),
|
|
14
|
+
user_agent character varying(1000)
|
|
15
|
+
);
|
|
16
|
+
CREATE TABLE email (
|
|
17
|
+
id seriall primary key,
|
|
18
|
+
to_email character varying(254) NOT NULL,
|
|
19
|
+
to_name character varying(254),
|
|
20
|
+
to_person_id integer,
|
|
21
|
+
from_email character varying(254) NOT NULL,
|
|
22
|
+
from_name character varying(254),
|
|
23
|
+
from_person_id integer,
|
|
24
|
+
reply_to_name character varying(254),
|
|
25
|
+
reply_to_email character varying(254),
|
|
26
|
+
subject character varying(254) NOT NULL,
|
|
27
|
+
body_text text,
|
|
28
|
+
body_html text,
|
|
29
|
+
headers text,
|
|
30
|
+
email_type character varying(254),
|
|
31
|
+
document_id integer,
|
|
32
|
+
process_status character varying(32) DEFAULT 'draft'::character varying NOT NULL,
|
|
33
|
+
process_id integer,
|
|
34
|
+
process_start timestamp with time zone,
|
|
35
|
+
process_end timestamp with time zone,
|
|
36
|
+
process_error text,
|
|
37
|
+
create_date timestamp without time zone DEFAULT now() NOT NULL,
|
|
38
|
+
mod_date timestamp without time zone DEFAULT now() NOT NULL,
|
|
39
|
+
create_by_id integer,
|
|
40
|
+
mod_by_id integer,
|
|
41
|
+
cc character varying(4000),
|
|
42
|
+
bcc character varying(4000)
|
|
43
|
+
);
|
|
44
|
+
CREATE TABLE grp (
|
|
45
|
+
id serial primary key,
|
|
46
|
+
name character varying(64),
|
|
47
|
+
description character varying(254),
|
|
48
|
+
create_date timestamp without time zone DEFAULT now(),
|
|
49
|
+
mod_date timestamp without time zone DEFAULT now(),
|
|
50
|
+
create_by_id integer,
|
|
51
|
+
mod_by_id integer,
|
|
52
|
+
CONSTRAINT grp_uniq UNIQUE(name)
|
|
53
|
+
);
|
|
54
|
+
INSERT INTO grp (name, description, create_by_id, mod_by_id)
|
|
55
|
+
VALUES ('admin', 'People who can do everything', 1, 1),
|
|
56
|
+
('dev', 'People who can do everything else', 1, 1);
|
|
57
|
+
CREATE TABLE usr (
|
|
58
|
+
id serial primary key,
|
|
59
|
+
usr_name character varying(254) NOT NULL,
|
|
60
|
+
password_sha256 character varying(64),
|
|
61
|
+
salt_aes256 character varying(254),
|
|
62
|
+
failed_login_count smallint DEFAULT 0 NOT NULL,
|
|
63
|
+
require_new_password boolean NOT NULL DEFAULT false,
|
|
64
|
+
password_mod_date date DEFAULT now() NOT NULL,
|
|
65
|
+
last_login_host character varying(254),
|
|
66
|
+
last_login_date timestamp without time zone,
|
|
67
|
+
create_date timestamp without time zone DEFAULT now(),
|
|
68
|
+
mod_date timestamp without time zone DEFAULT now(),
|
|
69
|
+
create_by_id integer,
|
|
70
|
+
mod_by_id integer,
|
|
71
|
+
key character varying(32) DEFAULT generate_key(),
|
|
72
|
+
key_sent_date timestamp without time zone,
|
|
73
|
+
CONSTRAINT usr_uniq UNIQUE(usr_name)
|
|
74
|
+
);
|
|
75
|
+
CREATE TABLE usr_grp (
|
|
76
|
+
id serial primary key,
|
|
77
|
+
usr_id integer NOT NULL,
|
|
78
|
+
grp_id integer NOT NULL,
|
|
79
|
+
create_date timestamp without time zone DEFAULT now(),
|
|
80
|
+
mod_date timestamp without time zone DEFAULT now(),
|
|
81
|
+
create_by_id integer,
|
|
82
|
+
mod_by_id integer,
|
|
83
|
+
CONSTRAINT usr_grp_uniq UNIQUE(usr_id, grp_id)
|
|
84
|
+
);
|
|
85
|
+
INSERT INTO usr_grp (usr_id, grp_id, create_by_id, mod_by_id)
|
|
86
|
+
VALUES (1, 1, 1, 1),
|
|
87
|
+
(1, 2, 1, 1);
|
|
88
|
+
COMMIT;
|
data/skel/lib/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Put locally install libs here.
|
data/skel/log/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
waxx log files are stored here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Use this to store files that will be uploaded and downloaded privately (requiring a password or something).
|