punk 0.1.2 → 0.3.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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/workflows/ship.yml +3 -1
  4. data/.github/workflows/test.yml +26 -1
  5. data/.rdoc_options +23 -0
  6. data/.rgignore +1 -0
  7. data/.rspec +2 -0
  8. data/Gemfile +7 -8
  9. data/Gemfile.lock +28 -41
  10. data/README.md +7 -1
  11. data/Rakefile +0 -1
  12. data/VERSION +1 -1
  13. data/app/migrations/001_lets_punk.rb +3 -0
  14. data/app/routes/hello.rb +4 -0
  15. data/env/.gitignore +3 -0
  16. data/env/spec/test.sh +3 -0
  17. data/lib/punk/actions/.keep +0 -0
  18. data/lib/punk/actions/groups/list.rb +24 -0
  19. data/lib/punk/actions/sessions/clear.rb +21 -0
  20. data/lib/punk/actions/sessions/create.rb +64 -0
  21. data/lib/punk/actions/sessions/list.rb +18 -0
  22. data/lib/punk/actions/sessions/verify.rb +24 -0
  23. data/lib/punk/actions/tenants/list.rb +18 -0
  24. data/lib/punk/actions/users/list_group.rb +18 -0
  25. data/lib/punk/actions/users/list_tenant.rb +18 -0
  26. data/lib/punk/actions/users/show.rb +18 -0
  27. data/lib/punk/commands/list.rb +12 -6
  28. data/lib/punk/config/defaults.json +3 -0
  29. data/lib/punk/config/schema.json +3 -0
  30. data/lib/punk/core/app.rb +4 -6
  31. data/lib/punk/core/commander.rb +12 -7
  32. data/lib/punk/core/exec.rb +2 -0
  33. data/lib/punk/core/load.rb +0 -1
  34. data/lib/punk/framework/command.rb +5 -1
  35. data/lib/punk/framework/plugins/validation.rb +0 -14
  36. data/lib/punk/helpers/loggable.rb +1 -1
  37. data/lib/punk/migrations/001_punk.rb +103 -0
  38. data/lib/punk/models/.keep +0 -0
  39. data/lib/punk/models/group.rb +20 -0
  40. data/lib/punk/models/group_user_metadata.rb +17 -0
  41. data/lib/punk/models/identity.rb +29 -0
  42. data/lib/punk/models/session.rb +89 -0
  43. data/lib/punk/models/tenant.rb +19 -0
  44. data/lib/punk/models/tenant_user_metadata.rb +17 -0
  45. data/lib/punk/models/user.rb +31 -0
  46. data/lib/punk/routes/groups.rb +31 -0
  47. data/lib/punk/routes/plivo.rb +4 -0
  48. data/lib/punk/routes/sessions.rb +108 -0
  49. data/lib/punk/routes/swagger.rb +9 -0
  50. data/lib/punk/routes/tenants.rb +29 -0
  51. data/lib/punk/routes/users.rb +36 -0
  52. data/lib/punk/services/.keep +0 -0
  53. data/lib/punk/services/challenge_claim.rb +46 -0
  54. data/lib/punk/services/create_identities.rb +25 -0
  55. data/lib/punk/services/generate_swagger.rb +25 -0
  56. data/lib/punk/services/prove_claim.rb +29 -0
  57. data/lib/punk/services/secret.rb +9 -0
  58. data/lib/punk/templates/groups/list.jbuilder +7 -0
  59. data/lib/punk/templates/plivo.slim +16 -0
  60. data/lib/punk/templates/sessions/list.jbuilder +6 -0
  61. data/lib/punk/templates/sessions/pending.jbuilder +4 -0
  62. data/lib/punk/templates/tenants/list.jbuilder +7 -0
  63. data/lib/punk/templates/tenants/list.slim +8 -0
  64. data/lib/punk/templates/users/list.jbuilder +7 -0
  65. data/lib/punk/templates/users/list.rcsv +4 -0
  66. data/lib/punk/templates/users/show.jbuilder +5 -0
  67. data/lib/punk/views/groups/list.rb +22 -0
  68. data/lib/punk/views/plivo_store.rb +15 -0
  69. data/lib/punk/views/sessions/list.rb +22 -0
  70. data/lib/punk/views/sessions/pending.rb +28 -0
  71. data/lib/punk/views/tenants/list.rb +22 -0
  72. data/lib/punk/views/users/list.rb +22 -0
  73. data/lib/punk/views/users/show.rb +22 -0
  74. data/lib/punk/workers/.keep +0 -0
  75. data/lib/punk/workers/expire_sessions.rb +9 -0
  76. data/lib/punk/workers/geocode_session_worker.rb +48 -0
  77. data/lib/punk/workers/identify_session_worker.rb +45 -0
  78. data/lib/punk/workers/secret.rb +18 -0
  79. data/lib/punk/workers/send_email_worker.rb +51 -0
  80. data/lib/punk/workers/send_sms_worker.rb +40 -0
  81. data/punk.gemspec +147 -21
  82. data/schema.psql +345 -0
  83. data/spec/actions/groups/punk/list_groups_action_spec.rb +36 -0
  84. data/spec/actions/sessions/punk/clear_session_action_spec.rb +29 -0
  85. data/spec/actions/sessions/punk/create_session_action_spec.rb +33 -0
  86. data/spec/actions/sessions/punk/list_sessions_action_spec.rb +26 -0
  87. data/spec/actions/sessions/punk/verify_session_action_spec.rb +59 -0
  88. data/spec/actions/tenants/punk/list_tenants_action_spec.rb +25 -0
  89. data/spec/actions/users/punk/list_group_users_action_spec.rb +26 -0
  90. data/spec/actions/users/punk/list_tenant_users_action_spec.rb +26 -0
  91. data/spec/factories/group.rb +12 -0
  92. data/spec/factories/group_user_metadata.rb +10 -0
  93. data/spec/factories/identity.rb +19 -0
  94. data/spec/factories/session.rb +12 -0
  95. data/spec/factories/tenant.rb +10 -0
  96. data/spec/factories/tenant_user_metadata.rb +10 -0
  97. data/spec/factories/user.rb +12 -0
  98. data/spec/lib/commands/auth_spec.rb +11 -0
  99. data/spec/lib/commands/generate_spec.rb +7 -0
  100. data/spec/lib/commands/http_spec.rb +23 -0
  101. data/spec/lib/commands/list_spec.rb +7 -0
  102. data/spec/lib/commands/swagger_spec.rb +7 -0
  103. data/spec/lib/engine/punk_env_spec.rb +13 -0
  104. data/spec/lib/engine/punk_exec_spec.rb +9 -0
  105. data/spec/lib/engine/punk_init_spec.rb +9 -0
  106. data/spec/lib/engine/punk_store_spec.rb +10 -0
  107. data/spec/lib/punk.env +7 -0
  108. data/spec/models/punk/group_spec.rb +50 -0
  109. data/spec/models/punk/group_user_metadata_spec.rb +61 -0
  110. data/spec/models/punk/identity_spec.rb +61 -0
  111. data/spec/models/punk/session_spec.rb +156 -0
  112. data/spec/models/punk/tenant_spec.rb +51 -0
  113. data/spec/models/punk/tenant_user_metadata_spec.rb +61 -0
  114. data/spec/models/punk/user_spec.rb +115 -0
  115. data/spec/routes/groups/get_groups_spec.rb +33 -0
  116. data/spec/routes/plivo/get_plivo_spec.rb +11 -0
  117. data/spec/routes/sessions/delete_session_spec.rb +11 -0
  118. data/spec/routes/sessions/get_sessions_spec.rb +30 -0
  119. data/spec/routes/sessions/patch_session_spec.rb +11 -0
  120. data/spec/routes/sessions/post_session_spec.rb +11 -0
  121. data/spec/routes/swagger/get_swagger_spec.rb +12 -0
  122. data/spec/routes/tenants/get_tenants_spec.rb +31 -0
  123. data/spec/routes/users/get_users_spec.rb +60 -0
  124. data/spec/services/punk/challenge_claim_service_spec.rb +7 -0
  125. data/spec/services/punk/create_identities_service_spec.rb +14 -0
  126. data/spec/services/punk/generate_swagger_service_spec.rb +7 -0
  127. data/spec/services/punk/prove_claim_service_spec.rb +7 -0
  128. data/spec/services/punk/secret_service_spec.rb +7 -0
  129. data/spec/spec_helper.rb +122 -0
  130. data/spec/vcr_cassettes/PUNK_GeocodeSessionWorker/updates_the_session_data.yml +57 -0
  131. data/spec/vcr_cassettes/PUNK_IdentifySessionWorker/updates_the_session_data.yml +112 -0
  132. data/spec/views/punk/plivo_store_spec.rb +7 -0
  133. data/spec/views/sessions/punk/list_sessions_view_spec.rb +7 -0
  134. data/spec/views/sessions/punk/pending_session_view_spec.rb +7 -0
  135. data/spec/views/tenants/punk/list_tenants_view_spec.rb +7 -0
  136. data/spec/views/users/punk/list_groups_view_spec.rb +7 -0
  137. data/spec/views/users/punk/list_users_view_spec.rb +7 -0
  138. data/spec/workers/punk/expire_sessions_worker_spec.rb +31 -0
  139. data/spec/workers/punk/geocode_session_worker_spec.rb +14 -0
  140. data/spec/workers/punk/identify_session_worker_spec.rb +15 -0
  141. data/spec/workers/punk/secret_worker_spec.rb +20 -0
  142. data/spec/workers/punk/send_email_worker_spec.rb +46 -0
  143. data/spec/workers/punk/send_sms_worker_spec.rb +33 -0
  144. metadata +154 -16
  145. data/lib/punk/views/all.rb +0 -4
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ClearSessionAction < Action
5
+ args :session
6
+
7
+ def validate
8
+ validates_not_null :session
9
+ validates_not_empty :session
10
+ return if session.blank?
11
+ validates_type Session, :session
12
+ validates_state :session, :active
13
+ validates_event :session, :clear
14
+ end
15
+
16
+ def process
17
+ session.clear!
18
+ present Info, message: "You have been logged out."
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'phony'
5
+
6
+ module PUNK
7
+ class CreateSessionAction < Action
8
+ args :claim, :remote_addr, :user_agent
9
+
10
+ def validate
11
+ validates_not_null :claim
12
+ validates_not_empty :claim
13
+ validates_not_null :claim_type, message: "is not an email address or phone number"
14
+ validates_email :claim if claim_type == :email
15
+ validates_phone :claim if claim_type == :phone
16
+ validates_not_null :remote_addr
17
+ validates_not_empty :remote_addr
18
+ validates_not_null :user_agent
19
+ validates_not_empty :user_agent
20
+ end
21
+
22
+ def process
23
+ PUNK.db.transaction do
24
+ identity =
25
+ Identity.find_or_create(claim: _normalize_claim) do |i|
26
+ i.claim_type = claim_type
27
+ end
28
+ session = Session.create(identity: identity, remote_addr: remote_addr, user_agent: user_agent)
29
+ ChallengeClaimService.run(session: session)
30
+ IdentifySessionWorker.perform_async(session_id: session.id)
31
+ message =
32
+ case identity.claim_type
33
+ when :email
34
+ "We have sent a verification code to your email address. Please enter it to verify your identity."
35
+ when :phone
36
+ "We have sent a verification code to your phone number by SMS. Please enter it to verify your identity."
37
+ end
38
+ present PendingSessionView, session: session, message: message, status: 201
39
+ end
40
+ end
41
+
42
+ def claim_type
43
+ @claim_type ||= _guess_claim_type
44
+ end
45
+
46
+ private
47
+
48
+ def _guess_claim_type
49
+ return :email if URI::MailTo::EMAIL_REGEXP.match(claim)
50
+ return :phone if Phony.plausible?(claim)
51
+ end
52
+
53
+ def _normalize_claim
54
+ case claim_type
55
+ when :email
56
+ claim.downcase.strip
57
+ when :phone
58
+ phone = claim.strip
59
+ phone = "+1#{phone}" if phone !~ /^[+]/
60
+ "+#{Phony.normalize(phone)}"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListSessionsAction < Action
5
+ args :user
6
+
7
+ def validate
8
+ validates_not_null :user
9
+ validates_not_empty :user
10
+ return if user.blank?
11
+ validates_type User, :user
12
+ end
13
+
14
+ def process
15
+ present ListSessionsView, sessions: user.active_sessions.all
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class VerifySessionAction < Action
5
+ args :session, :secret
6
+
7
+ def validate
8
+ validates_not_null :session
9
+ validates_not_empty :session
10
+ return if session.blank?
11
+ validates_not_null :secret
12
+ return if secret.blank?
13
+ validates_type Session, :session
14
+ validates_state :session, :pending
15
+ validates_event :session, :verify
16
+ end
17
+
18
+ def process
19
+ verify = ProveClaimService.run(session: session, secret: secret)
20
+ raise BadRequest, 'Secret is incorrect' unless verify.result == true
21
+ present Info, message: 'We have succesfully verified your identity. Welcome to GroupFire!'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListTenantsAction < Action
5
+ args :user
6
+
7
+ def validate
8
+ validates_not_null :user
9
+ validates_not_empty :user
10
+ return if user.blank?
11
+ validates_type User, :user
12
+ end
13
+
14
+ def process
15
+ present ListTenantsView, tenants: user.tenants_dataset.order(:name).all
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListGroupUsersAction < Action
5
+ args :group
6
+
7
+ def validate
8
+ validates_not_null :group
9
+ validates_not_empty :group
10
+ return if group.blank?
11
+ validates_type Group, :group
12
+ end
13
+
14
+ def process
15
+ present ListUsersView, users: group.users
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ListTenantUsersAction < Action
5
+ args :tenant
6
+
7
+ def validate
8
+ validates_not_null :tenant
9
+ validates_not_empty :tenant
10
+ return if tenant.blank?
11
+ validates_type Tenant, :tenant
12
+ end
13
+
14
+ def process
15
+ present ListUsersView, users: tenant.users
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PUNK
4
+ class ShowUserAction < Action
5
+ args :user
6
+
7
+ def validate
8
+ validates_not_null :user
9
+ validates_not_empty :user
10
+ return if user.blank?
11
+ validates_type User, :user
12
+ end
13
+
14
+ def process
15
+ present ShowUserView, user: user
16
+ end
17
+ end
18
+ end
@@ -4,25 +4,31 @@ PUNK::Command.create "list" do
4
4
  shortcut 'l'
5
5
  description "List routes, actions, models, views, services or workers"
6
6
 
7
+ option shortcut: :a, name: :all, description: "Include Punk!", type: nil
8
+
7
9
  def process
8
10
  case args.join(' ')
9
11
  when 'routes'
10
12
  PUNK.app
11
- PUNK::App.route_list
13
+ PUNK::App.route_list # TODO: exclude PUNK routes by default
12
14
  when 'actions'
13
- ObjectSpace.each_object(PUNK::Action.singleton_class).map(&:name).reject { |name| name.nil? || name =~ /^PUNK/ }
15
+ ObjectSpace.each_object(PUNK::Action.singleton_class).map(&:name).reject { |name| _hide?(name) }
14
16
  when 'models'
15
- ObjectSpace.each_object(PUNK::Model.singleton_class).map(&:name).reject { |name| name.nil? || name =~ /^PUNK/ }
17
+ ObjectSpace.each_object(PUNK::Model.singleton_class).map(&:name).reject { |name| _hide?(name) }
16
18
  when 'views'
17
- ObjectSpace.each_object(PUNK::View.singleton_class).map(&:name).reject { |name| name.nil? || name =~ /^PUNK/ }
19
+ ObjectSpace.each_object(PUNK::View.singleton_class).map(&:name).reject { |name| _hide?(name) }
18
20
  when 'services'
19
- ObjectSpace.each_object(PUNK::Service.singleton_class).select { |klass| klass.superclass == PUNK::Service }.map(&:name).reject { |name| name.nil? || name =~ /^PUNK/ }
21
+ ObjectSpace.each_object(PUNK::Service.singleton_class).select { |klass| klass.superclass == PUNK::Service }.map(&:name).reject { |name| _hide?(name) }
20
22
  when 'workers'
21
- ObjectSpace.each_object(PUNK::Worker.singleton_class).select { |klass| klass.superclass == PUNK::Worker }.map(&:name).reject { |name| name.nil? || name =~ /^PUNK/ }
23
+ ObjectSpace.each_object(PUNK::Worker.singleton_class).select { |klass| klass.superclass == PUNK::Worker }.map(&:name).reject { |name| _hide?(name) }
22
24
  when '', 'help'
23
25
  "? specify one of: routes, actions, models, views, services, workers"
24
26
  else
25
27
  "? unkown arguments: #{args.join(',')}"
26
28
  end
27
29
  end
30
+
31
+ def _hide?(name)
32
+ name.nil? || (name =~ /^PUNK/) && !opts[:all]
33
+ end
28
34
  end
@@ -30,6 +30,9 @@
30
30
  "api_key": "$USERSTACK_API_KEY",
31
31
  "use_ssl": false
32
32
  },
33
+ "ipstack": {
34
+ "api_key": "$IPSTACK_ACCESS_KEY"
35
+ },
33
36
  "db": {
34
37
  "url": "$DATABASE_URL"
35
38
  },
@@ -32,6 +32,9 @@
32
32
  "api_key": "String",
33
33
  "use_ssl": "Flag"
34
34
  },
35
+ "ipstack": {
36
+ "api_key": "String"
37
+ },
35
38
  "log": {
36
39
  "enabled!": "Flag",
37
40
  "type!": "Enum(stdout, stderr, file)",
@@ -23,7 +23,7 @@ module PUNK
23
23
 
24
24
  ROUTES = Tempfile.new("routes.json").path
25
25
  PUNK.profile_info("generate", path: ROUTES) do
26
- system "roda-parse_routes -f #{ROUTES} #{File.expand_path(File.join(PUNK.get.app.path, 'routes', '*'))}"
26
+ system "roda-parse_routes -f #{ROUTES} #{File.expand_path(File.join(PUNK.get.app.path, 'routes', '*'))} #{File.expand_path(File.join(__dir__, '..', 'routes', '*'))}"
27
27
  end
28
28
 
29
29
  class App < Roda
@@ -88,8 +88,7 @@ module PUNK
88
88
 
89
89
  def require_session!
90
90
  begin
91
- # TODO
92
- @_current_session = nil # Session[request.session['session_id']]
91
+ @_current_session = Session[request.session['session_id']]
93
92
  if @_current_session&.active?
94
93
  @_current_session.touch
95
94
  else
@@ -192,9 +191,8 @@ module PUNK
192
191
  name = "#{request.request_method} #{request.path}"
193
192
  logger.info "Started #{name} for #{request.ip}", params.deep_symbolize_keys.sanitize.inspect
194
193
  logger.trace request.env['HTTP_USER_AGENT']
195
- # TODO
196
- # logger.info "Started #{name} for #{request.ip || Session.default_values[:remote_addr].to_s}", params.deep_symbolize_keys.sanitize.inspect
197
- # logger.trace request.env['HTTP_USER_AGENT'] || Session.default_values[:user_agent]
194
+ logger.info "Started #{name} for #{request.ip || Session.default_values[:remote_addr].to_s}", params.deep_symbolize_keys.sanitize.inspect
195
+ logger.trace request.env['HTTP_USER_AGENT'] || Session.default_values[:user_agent]
198
196
  logger.trace request.env['HTTP_COOKIE']
199
197
  logger.push_tags(name)
200
198
  _set_cookie(request.env)
@@ -10,7 +10,7 @@ command :test do |c|
10
10
  end
11
11
  ENV.delete_if { |name, _value| name =~ /^PUNK_/ }
12
12
  system('rubocop') &&
13
- # system('quasar build -m pwa') && TODO
13
+ # system('quasar build -m pwa') &&
14
14
  system('PUNK_ENV=test rspec')
15
15
  exit $CHILD_STATUS.exitstatus # rubocop:disable Rails/Exit
16
16
  end
@@ -74,12 +74,17 @@ command 'db migrate' do |c|
74
74
  say('Migrating db...')
75
75
  PUNK.boot
76
76
  Sequel.extension :migration
77
- if options.relative.nil?
78
- Sequel::Migrator.run(PUNK.db, File.join(PUNK.get.app.path, 'migrations'))
79
- else
80
- Sequel::Migrator.run(PUNK.db, File.join(PUNK.get.app.path, 'migrations'), relative: options.relative)
77
+ migrations_path = File.join(PUNK.get.app.path, 'migrations')
78
+ if File.exist?(migrations_path)
79
+ if options.relative.nil?
80
+ Sequel::Migrator.run(PUNK.db, migrations_path)
81
+ else
82
+ Sequel::Migrator.run(PUNK.db, migrations_path, relative: options.relative)
83
+ end
84
+ end
85
+ if PUNK.env.development?
86
+ database = File.basename(PUNK.get.db.url)
87
+ `pg_dump #{database} --schema-only > schema.psql`
81
88
  end
82
- database = File.basename(PUNK.get.db.url)
83
- `pg_dump #{database} --schema-only > schema.psql`
84
89
  end
85
90
  end
@@ -23,6 +23,7 @@ end
23
23
 
24
24
  PUNK::Interface.register(:app) do
25
25
  require_relative 'app'
26
+ PUNK.require_all(File.join(__dir__, '..', 'routes'))
26
27
  PUNK.require_all(File.join(PUNK.get.app.path, 'routes'))
27
28
  retval = PUNK.get.app.reloadable ? PUNK.loader : PUNK::App.freeze.app
28
29
  SemanticLogger.flush
@@ -32,6 +33,7 @@ end
32
33
  PUNK.inject :loader, :app
33
34
 
34
35
  ['actions', 'models', 'views', 'services', 'workers'].each do |dir|
36
+ PUNK.require_all(File.join(__dir__, '..', dir))
35
37
  PUNK.require_all(File.join(PUNK.get.app.path, dir))
36
38
  end
37
39
 
@@ -4,6 +4,5 @@ PUNK.db
4
4
 
5
5
  require_relative '../helpers/all'
6
6
  require_relative '../framework/all'
7
- require_relative '../views/all'
8
7
 
9
8
  PUNK.store[:state] = :loaded
@@ -71,7 +71,11 @@ module PUNK
71
71
  define_method(:options) do |opt|
72
72
  punk_command = PUNK.store.commands[match]
73
73
  punk_command.instance_variable_get(:@options).each_value do |option|
74
- opt.on option[:shortcut], option[:name], option[:description], argument: true, as: option[:type]
74
+ if option[:type].present?
75
+ opt.on option[:shortcut], option[:name], option[:description], argument: true, as: option[:type]
76
+ else
77
+ opt.on option[:shortcut], option[:name], option[:description]
78
+ end
75
79
  end
76
80
  end
77
81
  define_method(:process) do
@@ -28,20 +28,6 @@ module PUNK
28
28
  end
29
29
  end
30
30
 
31
- def validates_parse_id(atts, opts={})
32
- default = { message: "is not a Parse ID" }
33
- validatable_attributes(atts, default.merge(opts)) do |_name, value, message|
34
- message unless /^[[:alnum:]]{10}$/.match(value)
35
- end
36
- end
37
-
38
- def validates_subdomain(atts, opts={})
39
- default = { message: "is not a subdomain" }
40
- validatable_attributes(atts, default.merge(opts)) do |_name, value, message|
41
- message unless /^[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?$/.match(value)
42
- end
43
- end
44
-
45
31
  def validates_state(name, state)
46
32
  errors.add(name, "is not in #{state} state") unless self[name].send("#{state}?")
47
33
  end
@@ -22,7 +22,7 @@ module PUNK
22
22
 
23
23
  def exception(e, extra={})
24
24
  if ENV.key?('SENTRY_DSN')
25
- ::Raven.capture_exception(
25
+ ::Sentry.capture_exception(
26
26
  e,
27
27
  message: e.message,
28
28
  extra: extra,
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ def _tenants
4
+ create_table :tenants do
5
+ uuid :id, primary_key: true, default: Sequel.function(:gen_random_uuid)
6
+ punk_state :state, null: false, default: 'created'
7
+ String :name, null: false, text: true
8
+ String :icon, text: true
9
+ jsonb :data, default: '{}'
10
+ DateTime :created_at
11
+ DateTime :updated_at
12
+ end
13
+ end
14
+
15
+ def _users
16
+ create_table :users do
17
+ uuid :id, primary_key: true, default: Sequel.function(:gen_random_uuid)
18
+ punk_state :state, null: false, default: 'created'
19
+ String :name, null: false, text: true
20
+ String :icon, text: true
21
+ String :email, text: true, unique: true
22
+ String :phone, text: true, unique: true
23
+ jsonb :data, default: '{}'
24
+ DateTime :created_at
25
+ DateTime :updated_at
26
+ end
27
+ end
28
+
29
+ def _tenants_users
30
+ create_table :tenants_users do
31
+ primary_key [:tenant_id, :user_id]
32
+ foreign_key :tenant_id, :tenants, null: false, type: :uuid
33
+ foreign_key :user_id, :users, null: false, type: :uuid
34
+ index [:tenant_id, :user_id]
35
+ end
36
+ end
37
+
38
+ def _groups
39
+ create_table :groups do
40
+ uuid :id, primary_key: true, default: Sequel.function(:gen_random_uuid)
41
+ punk_state :state, null: false, default: 'created'
42
+ String :name, null: false, text: true
43
+ String :icon, text: true
44
+ jsonb :data, default: '{}'
45
+ DateTime :created_at
46
+ DateTime :updated_at
47
+ foreign_key :tenant_id, :tenants, null: false, type: :uuid
48
+ end
49
+ end
50
+
51
+ def _groups_users
52
+ create_table :groups_users do
53
+ primary_key [:group_id, :user_id]
54
+ foreign_key :group_id, :groups, null: false, type: :uuid
55
+ foreign_key :user_id, :users, null: false, type: :uuid
56
+ index [:group_id, :user_id]
57
+ end
58
+ end
59
+
60
+ def _identities
61
+ create_enum(:claim_type, %w[email phone])
62
+ create_table :identities do
63
+ uuid :id, primary_key: true, default: Sequel.function(:gen_random_uuid)
64
+ punk_state :state, null: false, default: 'created'
65
+ claim_type :claim_type, null: false
66
+ String :claim, text: true, null: false, unique: true
67
+ jsonb :data, default: '{}'
68
+ DateTime :created_at
69
+ DateTime :updated_at
70
+ foreign_key :user_id, :users, null: true, type: :uuid
71
+ end
72
+ end
73
+
74
+ def _sessions
75
+ create_enum(:session_state, %w[pending created active deleted expired])
76
+ create_table :sessions do
77
+ uuid :id, primary_key: true, default: Sequel.function(:gen_random_uuid)
78
+ uuid :slug, default: Sequel.function(:gen_random_uuid)
79
+ session_state :state, null: false, default: 'created'
80
+ File :salt, text: true
81
+ File :hash, text: true
82
+ Integer :attempt_count, null: false, default: 0
83
+ cidr :remote_addr, null: false, default: '127.0.0.1'
84
+ String :user_agent, text: true, null: false, default: 'Mozilla/5.0 (compatible; Punk!; +https://punk.kranzky.com)'
85
+ jsonb :data, default: '{}'
86
+ DateTime :created_at
87
+ DateTime :updated_at
88
+ foreign_key :identity_id, :identities, null: false, type: :uuid
89
+ end
90
+ end
91
+
92
+ PUNK.migration do
93
+ change do
94
+ create_enum(:punk_state, %w[created active deleted])
95
+ _tenants
96
+ _users
97
+ _tenants_users
98
+ _groups
99
+ _groups_users
100
+ _identities
101
+ _sessions
102
+ end
103
+ end