webhookdb 1.2.2 → 1.3.1
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/admin-dist/assets/index-6aebf805.js +264 -0
- data/admin-dist/favicon.ico +0 -0
- data/admin-dist/index.html +130 -0
- data/admin-dist/manifest.json +15 -0
- data/data/messages/replicators/url-recorder.liquid +20 -0
- data/data/messages/templates/errors/signalwire_send_sms.email.liquid +31 -0
- data/data/messages/web/install-customer-login.liquid +6 -5
- data/data/messages/web/install-error.liquid +1 -1
- data/data/messages/web/install-forbidden.liquid +25 -0
- data/data/messages/web/install-org-chooser.liquid +40 -0
- data/data/messages/web/install-success.liquid +2 -1
- data/data/messages/web/install.liquid +2 -1
- data/data/messages/web/partials/head.liquid +2 -0
- data/data/messages/web/styles.liquid +24 -0
- data/db/migrations/041_views.rb +20 -0
- data/db/migrations/042_sint_lock.rb +10 -0
- data/db/migrations/043_text_search.rb +28 -0
- data/db/migrations/044_oauth_session_token_cache.rb +21 -0
- data/integration/auth_spec.rb +2 -2
- data/lib/sequel/plugins/text_searchable.rb +165 -0
- data/lib/sequel/text_searchable.rb +42 -0
- data/lib/webhookdb/admin_api/auth.rb +24 -3
- data/lib/webhookdb/admin_api/data_provider.rb +196 -0
- data/lib/webhookdb/admin_api/entities.rb +143 -28
- data/lib/webhookdb/admin_api.rb +0 -2
- data/lib/webhookdb/api/auth.rb +5 -6
- data/lib/webhookdb/api/db.rb +31 -6
- data/lib/webhookdb/api/entities.rb +7 -1
- data/lib/webhookdb/api/helpers.rb +6 -25
- data/lib/webhookdb/api/install.rb +204 -79
- data/lib/webhookdb/api/organizations.rb +14 -12
- data/lib/webhookdb/api/saved_queries.rb +9 -3
- data/lib/webhookdb/api/saved_views.rb +99 -0
- data/lib/webhookdb/api/service_integrations.rb +15 -9
- data/lib/webhookdb/api/subscriptions.rb +3 -1
- data/lib/webhookdb/api/sync_targets.rb +9 -7
- data/lib/webhookdb/api/system.rb +1 -0
- data/lib/webhookdb/api/webhook_subscriptions.rb +3 -1
- data/lib/webhookdb/apps.rb +30 -7
- data/lib/webhookdb/async/audit_logger.rb +2 -0
- data/lib/webhookdb/async.rb +5 -0
- data/lib/webhookdb/backfill_job/service_integration_lock.rb +22 -0
- data/lib/webhookdb/backfill_job.rb +9 -0
- data/lib/webhookdb/customer.rb +5 -0
- data/lib/webhookdb/database_document.rb +1 -1
- data/lib/webhookdb/db_adapter/default_sql.rb +1 -1
- data/lib/webhookdb/db_adapter.rb +20 -4
- data/lib/webhookdb/fixtures/message_bodies.rb +34 -0
- data/lib/webhookdb/fixtures/organizations.rb +5 -0
- data/lib/webhookdb/fixtures/roles.rb +14 -0
- data/lib/webhookdb/fixtures/saved_views.rb +25 -0
- data/lib/webhookdb/fixtures/webhook_subscription_deliveries.rb +18 -0
- data/lib/webhookdb/http.rb +8 -2
- data/lib/webhookdb/icalendar.rb +3 -0
- data/lib/webhookdb/idempotency.rb +69 -22
- data/lib/webhookdb/increase.rb +69 -21
- data/lib/webhookdb/intercom.rb +10 -3
- data/lib/webhookdb/jobs/backfill.rb +3 -1
- data/lib/webhookdb/jobs/emailer.rb +0 -1
- data/lib/webhookdb/jobs/icalendar_delete_stale_cancelled_events.rb +19 -0
- data/lib/webhookdb/jobs/icalendar_enqueue_syncs.rb +1 -1
- data/lib/webhookdb/jobs/icalendar_sync.rb +1 -1
- data/lib/webhookdb/jobs/increase_event_handler.rb +20 -0
- data/lib/webhookdb/jobs/scheduled_backfills.rb +2 -1
- data/lib/webhookdb/jobs/sync_target_run_sync.rb +3 -1
- data/lib/webhookdb/message/body.rb +6 -4
- data/lib/webhookdb/message/delivery.rb +2 -0
- data/lib/webhookdb/messages/error_icalendar_fetch.rb +1 -2
- data/lib/webhookdb/messages/error_signalwire_send_sms.rb +48 -0
- data/lib/webhookdb/oauth/fake_provider.rb +44 -0
- data/lib/webhookdb/oauth/front_provider.rb +1 -2
- data/lib/webhookdb/oauth/increase_provider.rb +80 -0
- data/lib/webhookdb/oauth/intercom_provider.rb +3 -11
- data/lib/webhookdb/oauth/session.rb +20 -0
- data/lib/webhookdb/oauth.rb +7 -21
- data/lib/webhookdb/organization/alerting.rb +2 -0
- data/lib/webhookdb/organization/database_migration.rb +3 -0
- data/lib/webhookdb/organization.rb +37 -6
- data/lib/webhookdb/organization_membership.rb +14 -7
- data/lib/webhookdb/postgres.rb +2 -0
- data/lib/webhookdb/replicator/base.rb +1 -0
- data/lib/webhookdb/replicator/docgen.rb +9 -1
- data/lib/webhookdb/replicator/fake.rb +2 -3
- data/lib/webhookdb/replicator/front_signalwire_message_channel_app_v1.rb +49 -14
- data/lib/webhookdb/replicator/icalendar_calendar_v1.rb +97 -17
- data/lib/webhookdb/replicator/icalendar_event_v1.rb +104 -2
- data/lib/webhookdb/replicator/increase_account_number_v1.rb +6 -43
- data/lib/webhookdb/replicator/increase_account_transfer_v1.rb +7 -24
- data/lib/webhookdb/replicator/increase_account_v1.rb +7 -31
- data/lib/webhookdb/replicator/increase_ach_transfer_v1.rb +5 -43
- data/lib/webhookdb/replicator/increase_app_v1.rb +78 -0
- data/lib/webhookdb/replicator/increase_check_transfer_v1.rb +23 -29
- data/lib/webhookdb/replicator/increase_event_v1.rb +41 -0
- data/lib/webhookdb/replicator/increase_limit_v1.rb +9 -34
- data/lib/webhookdb/replicator/increase_transaction_v1.rb +5 -30
- data/lib/webhookdb/replicator/increase_v1_mixin.rb +58 -78
- data/lib/webhookdb/replicator/increase_wire_transfer_v1.rb +5 -24
- data/lib/webhookdb/replicator/intercom_contact_v1.rb +51 -4
- data/lib/webhookdb/replicator/intercom_conversation_v1.rb +42 -6
- data/lib/webhookdb/replicator/intercom_marketplace_root_v1.rb +2 -13
- data/lib/webhookdb/replicator/intercom_v1_mixin.rb +20 -16
- data/lib/webhookdb/replicator/oauth_refresh_access_token_mixin.rb +1 -1
- data/lib/webhookdb/replicator/sponsy_v1_mixin.rb +1 -1
- data/lib/webhookdb/replicator/transistor_episode_v1.rb +17 -0
- data/lib/webhookdb/replicator/url_recorder_v1.rb +137 -0
- data/lib/webhookdb/replicator/webhook_request.rb +4 -0
- data/lib/webhookdb/replicator.rb +8 -0
- data/lib/webhookdb/role.rb +5 -2
- data/lib/webhookdb/saved_query.rb +23 -0
- data/lib/webhookdb/saved_view.rb +73 -0
- data/lib/webhookdb/sentry.rb +2 -0
- data/lib/webhookdb/service/entities.rb +0 -4
- data/lib/webhookdb/service/helpers.rb +5 -0
- data/lib/webhookdb/service/middleware.rb +9 -0
- data/lib/webhookdb/service/types.rb +10 -8
- data/lib/webhookdb/service/validators.rb +1 -2
- data/lib/webhookdb/service/view_api.rb +1 -1
- data/lib/webhookdb/service_integration.rb +17 -15
- data/lib/webhookdb/spec_helpers/shared_examples_for_replicators.rb +8 -8
- data/lib/webhookdb/spec_helpers/whdb.rb +3 -2
- data/lib/webhookdb/subscription.rb +2 -0
- data/lib/webhookdb/sync_target.rb +10 -2
- data/lib/webhookdb/tasks/message.rb +3 -1
- data/lib/webhookdb/version.rb +1 -1
- data/lib/webhookdb/webhook_subscription/delivery.rb +2 -0
- data/lib/webhookdb/webhook_subscription.rb +2 -0
- metadata +57 -9
- data/lib/webhookdb/admin_api/customers.rb +0 -63
- data/lib/webhookdb/admin_api/message_deliveries.rb +0 -61
- data/lib/webhookdb/admin_api/roles.rb +0 -15
|
Binary file
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta
|
|
6
|
+
name="viewport"
|
|
7
|
+
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
|
|
8
|
+
/>
|
|
9
|
+
<meta name="theme-color" content="#000000" />
|
|
10
|
+
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAE4klEQVRYR61XCWwUVRj+phehFhFrFA0EsIVSrnCUUIWUgMhVhajBQtpEBbEgBhKQQzSEGO4oIAa25YhRK5eRQCAgQWuVWigFqYqiSFMIjchVgQLFXs/v7+yyM7uzMw/CSybtzvzX+4/vfc+A7lqrEhCNTBgYTJU0Ph34PORXv86/lfx2DAqFiMHXmGTU6Jg2PIU2qmTUYyaikEPZVp7yIqAgAW1DI1ZgmnHaTSdyAPkqnoYWUHk6d9ZSy3GokMJ/fLWS+ouQa9xysuEcQJ7qTKVtVOhzT47DlY5zM1mYYvwV+ik8gHzVk0L7+Tzu5jztYWB8EvBYPHDsEvAZTVfXu4Z7nl9HMBO/WqXsAZg7/97L+axuwLKngZiooKlzrPrwvcAfUv3I6zwzMdiaiWAAZs2LGYBr2vu2AUpfMp03KeA2d90yFjBoqZR7fGq39KDrKufXgYGeCAaQp5bQ+TteNf+gPzCLIdY1AiP3AIeZ/lXpQG4POqbn7uyck+5ZkAiXMwvzxJcZwFqOWjRO8FcLrwA+zwByugIVV4Hk7ab0sLbAgTHm/8/sAgoveFhRqCVW9MLrxmkzgDy1ns4nezmX7zNY/9WDzN2u+Ako+gdY2BcYwJatZTnafwFcqdOxBB/L8KaBTaoVgaaKATyoo9Y6BihnD3RsbZeWun/IgGYf1bHSLFODBjxhIF+N4w9/MvWUU4iH+0YDnSxBbDgBTDsE1Ht0oM1DEyYYWK98bIopeq5NqTgWrmI80M4PzDINSUz9GUesc7XskwwcoQh7W39NSgY2DDFHT9ZZdv2TWzmW+iYCkmUSAAcJj+jqdmWnlIwF2lhOhzU/szlLdS3Y5C5JAHJgxOmoJzHl32byHLa06w12fM8vmf6bpoVnOQ2NxIjCizoWUacdQAdiftHz9u6X2r/xHbDJcuAWPwcUczTn6U1DcwACG4+6xduCsHuQhvsTcAJLcGApnbx73K55IRtIYD77feV5LojiZa0mfIvI9zER0Lq2nAKyi8Jx/zJpSyKztbcSyDzgWYbmJlxHsaluomWE2TTL7qtrgc7s+tDjN5ZTUf2KmYFGjkQKZSpuuAaRbxCGs4iCFHVeMvP/0mi8pU03/8ndy6EdslLZnL9lBcdzYiHwiRsha0KWwYMogQfD37TlyPek/jWvAbHRQW/rSCkE9ULXYp4J84Wu+tfbJYRnIqTjEt4Yi3YmlPhYhijnMgjnqJpAemQJ71Q10HsHDx8L8qQTSb5hoz5gydQL+4Cd5yIE0ISNmGpMNgMQ5tuAXyKRzwIS8ewUu6FDzJnvd+A6cSCDs5/b3e78dgNPxgK2udPJKGQ1Bj2Cx7HYzlPLGMBcp3j7kAUdfpFoZSmDa2vx46cngVcPRkz/UhKS+fLVTsmAH/mut5PajFTya/KAKO+bBCpJVtJJTC4KxoYvQY5B4ZTMzIIrKc3uBHzEIBIj3BIEnErIC3OIjgFotvlXIEYiw5mUBiQ9aHkim2xiF2AsL2appOZxJChXiQtlQs0JTruriAFOnECcGxjuTssDQdz/i0k5OcfLeheTYCaEpr/HnzN1yKpjtYV8Amuo//7dXc2s1swRncNXgphavJGycjMuINNeKaPmNjEaPe1XF/Jah1EErKF8Q8wDW/JOQNf4/xk+R0mLfuCzh7di91PAb/Z/EtJ0nCBsdHkAAAAASUVORK5CYII=" />
|
|
11
|
+
<link rel="icon" type="image/png" sizes="16x16" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACHklEQVQ4T22TTUhUcRTFf0+DnNGgWkiKobORICiSoo+ViVJRJi2K2hY2k61y0SaoIGgjSETqTLkICqKNZWQgkW2i6MMkR8igKIKiMgoCxxGzf+f6VN5744X3de//nnfvued6RK3bbaCIJB4NOBJ6mn3U9Ujf10h5b4Ipftis08Uoo1OHWvVVXABsDsc/3a+So512b8pcPoAllzKgt50LiVVxuLwDymNwYRgGv4YgHzPJXgPxAdKuR1Cp4JG+RqheAeO/YV81VNyE3GwIJEPSS3lK3ij3awEUBcNPmmHslxr/ArcEVnEDJqZDALPKqPPIuCtyn4z23FQJ/bshtgw6RuD0yyVYcXQbwFuF1kXDler9wxHjDVZfh7zRV2jjBpCXf3kwVqIZDOyChiq4/R4OD0GiVLOcjCA4po2DvPpfBDBW7zbB/gR8+gPb++GbBvaiBervFxApgEgLzWvh3h6YEeOb+2BUUzDQ3FE4Kx46soEqHO+sgi6daFtwn9sE57fAyATU3fG961dC9iA8+KyRDoba6PIw6RZrjPPqO1YLvfX64wwcegg/xdAlCWrbGugZg7an8wC+KjVGs4xL65601zKNbfgA1K4KEzb1F7aqoqxamjNHWntxYkkpl5fARbXRUgNxAT7/DmdewbMfi6ARKZvf9iGuZYLjUVUGajEx92oPToWXKVitSdvTRjoa9ayZU5KndXYMiasMrd5o8Ph/oDCo9HTUhIAAAAAASUVORK5CYII=" />
|
|
12
|
+
<title>WebhookDB Admin</title>
|
|
13
|
+
<style>
|
|
14
|
+
body {
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
font-family: sans-serif;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.loader-container {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 0;
|
|
27
|
+
bottom: 0;
|
|
28
|
+
left: 0;
|
|
29
|
+
right: 0;
|
|
30
|
+
background-color: #2ba9ff;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* CSS Spinner from https://projects.lukehaas.me/css-loaders/ */
|
|
34
|
+
|
|
35
|
+
.loader {
|
|
36
|
+
color: #ffffff;
|
|
37
|
+
font-size: 90px;
|
|
38
|
+
text-indent: -9999em;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
width: 1em;
|
|
41
|
+
height: 1em;
|
|
42
|
+
border-radius: 50%;
|
|
43
|
+
margin: 72px auto;
|
|
44
|
+
position: relative;
|
|
45
|
+
-webkit-transform: translateZ(0);
|
|
46
|
+
-ms-transform: translateZ(0);
|
|
47
|
+
transform: translateZ(0);
|
|
48
|
+
-webkit-animation: load6 1.7s infinite ease, round 1.7s infinite ease;
|
|
49
|
+
animation: load6 1.7s infinite ease, round 1.7s infinite ease;
|
|
50
|
+
}
|
|
51
|
+
@-webkit-keyframes load6 {
|
|
52
|
+
0% {
|
|
53
|
+
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
|
54
|
+
}
|
|
55
|
+
5%,
|
|
56
|
+
95% {
|
|
57
|
+
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
|
58
|
+
}
|
|
59
|
+
10%,
|
|
60
|
+
59% {
|
|
61
|
+
box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em;
|
|
62
|
+
}
|
|
63
|
+
20% {
|
|
64
|
+
box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em;
|
|
65
|
+
}
|
|
66
|
+
38% {
|
|
67
|
+
box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em;
|
|
68
|
+
}
|
|
69
|
+
100% {
|
|
70
|
+
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
@keyframes load6 {
|
|
74
|
+
0% {
|
|
75
|
+
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
|
76
|
+
}
|
|
77
|
+
5%,
|
|
78
|
+
95% {
|
|
79
|
+
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
|
80
|
+
}
|
|
81
|
+
10%,
|
|
82
|
+
59% {
|
|
83
|
+
box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em, -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em, -0.297em -0.775em 0 -0.477em;
|
|
84
|
+
}
|
|
85
|
+
20% {
|
|
86
|
+
box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em, -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em, -0.749em -0.34em 0 -0.477em;
|
|
87
|
+
}
|
|
88
|
+
38% {
|
|
89
|
+
box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em, -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em, -0.82em -0.09em 0 -0.477em;
|
|
90
|
+
}
|
|
91
|
+
100% {
|
|
92
|
+
box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em, 0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
@-webkit-keyframes round {
|
|
96
|
+
0% {
|
|
97
|
+
-webkit-transform: rotate(0deg);
|
|
98
|
+
transform: rotate(0deg);
|
|
99
|
+
}
|
|
100
|
+
100% {
|
|
101
|
+
-webkit-transform: rotate(360deg);
|
|
102
|
+
transform: rotate(360deg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
@keyframes round {
|
|
106
|
+
0% {
|
|
107
|
+
-webkit-transform: rotate(0deg);
|
|
108
|
+
transform: rotate(0deg);
|
|
109
|
+
}
|
|
110
|
+
100% {
|
|
111
|
+
-webkit-transform: rotate(360deg);
|
|
112
|
+
transform: rotate(360deg);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
116
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
|
117
|
+
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;500;600;700&display=swap" rel="stylesheet">
|
|
118
|
+
<script type="module" crossorigin src="/admin/assets/index-6aebf805.js"></script>
|
|
119
|
+
</head>
|
|
120
|
+
|
|
121
|
+
<body>
|
|
122
|
+
<noscript> You need to enable JavaScript to run this app. </noscript>
|
|
123
|
+
<div id="root">
|
|
124
|
+
<div class="loader-container">
|
|
125
|
+
<div class="loader">Loading...</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</body>
|
|
129
|
+
|
|
130
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"short_name": "admin",
|
|
3
|
+
"name": "{{name}}",
|
|
4
|
+
"icons": [
|
|
5
|
+
{
|
|
6
|
+
"src": "favicon.ico",
|
|
7
|
+
"sizes": "64x64 32x32 24x24 16x16",
|
|
8
|
+
"type": "image/x-icon"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"start_url": "./index.html",
|
|
12
|
+
"display": "standalone",
|
|
13
|
+
"theme_color": "#000000",
|
|
14
|
+
"background_color": "#ffffff"
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Thanks! - WebhookDB</title>
|
|
5
|
+
{% include 'web/partials/head' %}
|
|
6
|
+
</head>
|
|
7
|
+
<body>
|
|
8
|
+
<div class="layout">
|
|
9
|
+
<div class="flex column align-items-center content">
|
|
10
|
+
<div class="text-center mt-2">
|
|
11
|
+
{{ content }}
|
|
12
|
+
</div>
|
|
13
|
+
<div class="text-center mt-2 flex column align-items-middle w-100">
|
|
14
|
+
<hr class="w-100"/>
|
|
15
|
+
<span class="mt-2 text-small">Hosted at <a href="https://webhookdb.com">https://webhookdb.com</a></span>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{% expose subject %}WebhookDB: Signalwire SMS Error{% endexpose %}
|
|
2
|
+
|
|
3
|
+
{% partial 'greeting' %}
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
WebhookDB encountered an error trying to send an SMS via Signalwire using your credentials. We have the following information about the failure:
|
|
7
|
+
</p>
|
|
8
|
+
<ul>
|
|
9
|
+
<li>Service Integration Name: {{ service_name }}, ID: <code>{{ opaque_id }}</code></li>
|
|
10
|
+
<li>Request: <code>{{ request_method }} {{ request_url }}</code></li>
|
|
11
|
+
<li>Response Status: <code>{{ response_status }}</code></li>
|
|
12
|
+
<li>Body: <code>{{ response_body }}</code></li>
|
|
13
|
+
</ul>
|
|
14
|
+
<p>
|
|
15
|
+
Usually this indicates an API key has been revoked, or the parameters to your integration (like the 'from' number)
|
|
16
|
+
are incorrect, rather than being an error in WebhookDB itself.
|
|
17
|
+
</p>
|
|
18
|
+
<p>To fix this integration, head over to <a href="{{ app_url }}">{{ app_url }}</a>, log in, and run:</p>
|
|
19
|
+
<pre>
|
|
20
|
+
webhookdb integrations reset {{ opaque_id }}
|
|
21
|
+
</pre>
|
|
22
|
+
<p>
|
|
23
|
+
If you no longer wish to use this integration, it can be deleted using:
|
|
24
|
+
</p>
|
|
25
|
+
<pre>
|
|
26
|
+
webhookdb integrations delete {{ opaque_id }}
|
|
27
|
+
</pre>
|
|
28
|
+
<p>We'll continue to send daily emails when this happens, so please do fix this up when you get a chance.</p>
|
|
29
|
+
<p>Please file an issue at {{ oss_repo }} if you need any help.</p>
|
|
30
|
+
|
|
31
|
+
{% partial 'signoff' %}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<html lang="en-US">
|
|
2
2
|
<head>
|
|
3
|
-
{
|
|
3
|
+
<title>WebhookDB | Sync {{ app_name }} | Login</title>
|
|
4
|
+
{% include 'web/partials/head' %}
|
|
4
5
|
</head>
|
|
5
6
|
<body>
|
|
6
7
|
<div class="layout">
|
|
@@ -11,12 +12,12 @@
|
|
|
11
12
|
<form method="POST" action="{{ action_url }}">
|
|
12
13
|
{% if view == "email" %}
|
|
13
14
|
<p>
|
|
14
|
-
Welcome to WebhookDB! In order to
|
|
15
|
-
|
|
15
|
+
Welcome to WebhookDB! In order to finish your sync with {{ app_name }},
|
|
16
|
+
you need to sign up or log in.
|
|
16
17
|
</p>
|
|
17
18
|
<p>Enter your email:</p>
|
|
18
19
|
<div class="input-group">
|
|
19
|
-
<input class="input-inline" type="text" id="email" name="email"/>
|
|
20
|
+
<input class="input-inline" type="text" id="email" name="email" autofocus/>
|
|
20
21
|
<input class="input-inline-button" type="submit" value="Log in"/>
|
|
21
22
|
</div>
|
|
22
23
|
{% partial 'web/partials/form_error' %}
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
</p>
|
|
32
33
|
<p>Enter the token from your email:</p>
|
|
33
34
|
<div class="input-group">
|
|
34
|
-
<input class="input-inline" type="text" id="otp_token" name="otp_token"/>
|
|
35
|
+
<input class="input-inline" type="text" id="otp_token" name="otp_token" autofocus/>
|
|
35
36
|
<input class="input-inline-button" type="submit" value="Log in"/>
|
|
36
37
|
</div>
|
|
37
38
|
{% partial 'web/partials/form_error' %}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<html lang="en-US">
|
|
2
|
+
<head>
|
|
3
|
+
<title>WebhookDB | Sync {{ app_name }} | Error</title>
|
|
4
|
+
{% include 'web/partials/head' %}
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<div class="layout">
|
|
8
|
+
<div class="flex column align-items-center content">
|
|
9
|
+
{% partial 'web/partials/header' %}
|
|
10
|
+
<div class="flex column">
|
|
11
|
+
<p>
|
|
12
|
+
Sorry, something went wrong installing your sync to {{ app_name }}.
|
|
13
|
+
</p>
|
|
14
|
+
<p>
|
|
15
|
+
If your installation finished successfully, head to <a href="{{ terminal_url }}">{{ terminal_url }}</a>
|
|
16
|
+
and run <code>webhookdb auth login</code> and <code>webhookdb db connection</code> to get your connection string.
|
|
17
|
+
</p>
|
|
18
|
+
<p>Or head to <a href="{{ install_url }}">{{ install_url }}</a> to go back through setup.</p>
|
|
19
|
+
<p>If you need any help, you can email <a href="mailto:hello@webhookdb.com">hello@webhookdb.com</a>.</p>
|
|
20
|
+
</div>
|
|
21
|
+
{% partial 'web/partials/footer' %}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<html lang="en-US">
|
|
2
|
+
<head>
|
|
3
|
+
<title>WebhookDB | Sync {{ app_name }} | Choose Organization</title>
|
|
4
|
+
{% include 'web/partials/head' %}
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<div class="layout">
|
|
8
|
+
<div class="flex column align-items-center content">
|
|
9
|
+
{% partial 'web/partials/header' %}
|
|
10
|
+
|
|
11
|
+
<div class="mt-2">
|
|
12
|
+
<form method="POST" action="{{ action_url }}">
|
|
13
|
+
<p>
|
|
14
|
+
Choose which WebhookDB organization your {{ app_name }} data will be replicated to.
|
|
15
|
+
You can also create a new organization (and database) to hold this data.
|
|
16
|
+
</p>
|
|
17
|
+
<div class="input-radio-group">
|
|
18
|
+
{% for org in organizations %}
|
|
19
|
+
<div class="input-radio-control">
|
|
20
|
+
<input type="radio" id="org-{{ org.key }}" name="existing_org_key" value="{{ org.key }}" checked="{{ org.checked }}" />
|
|
21
|
+
<label for="org-{{ org.key }}">{{ org.name }}</label>
|
|
22
|
+
</div>
|
|
23
|
+
{% endfor %}
|
|
24
|
+
<div class="input-radio-control">
|
|
25
|
+
<input type="radio" id="new_org" name="existing_org_key" value=""/>
|
|
26
|
+
<label for="new_org">Create an Organization:</label>
|
|
27
|
+
<input id="new_org_name" type="text" name="new_org_name" aria-labelledby="new_org" onfocus="document.querySelector('#new_org').checked = true">
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<input class="button w-100" type="submit" value="Submit"/>
|
|
31
|
+
{% partial 'web/partials/form_error' %}
|
|
32
|
+
<input type="hidden" id="state" name="state" value="{{ oauth_state }}">
|
|
33
|
+
</form>
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
{% partial 'web/partials/footer' %}
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
text-align: center;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
.text-small {
|
|
52
|
+
font-size: 80%;
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
.btn {
|
|
52
56
|
background-color: var(--color-primary);
|
|
53
57
|
border: none;
|
|
@@ -97,6 +101,12 @@
|
|
|
97
101
|
font-size: 16px;
|
|
98
102
|
padding-left: var(--spacing);
|
|
99
103
|
border-radius: var(--border-radius);
|
|
104
|
+
outline: none;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
input[type=text]:focus {
|
|
108
|
+
border: solid 1px var(--color-primary);
|
|
109
|
+
outline: solid 2px var(--color-primary) !important;
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
.input-inline {
|
|
@@ -122,6 +132,20 @@
|
|
|
122
132
|
border-radius: 0 var(--border-radius) var(--border-radius) 0 !important;
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
.input-radio-group {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-direction: column;
|
|
138
|
+
gap: 8px;
|
|
139
|
+
margin-bottom: 16px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.input-radio-control {
|
|
143
|
+
display: flex;
|
|
144
|
+
flex-direction: row;
|
|
145
|
+
align-items: center;
|
|
146
|
+
gap: 8px;
|
|
147
|
+
}
|
|
148
|
+
|
|
125
149
|
.form-error {
|
|
126
150
|
background-color: var(--color-warning);
|
|
127
151
|
border: solid 1px var(--color-warning-dark);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:saved_views) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
timestamptz :created_at, null: false, default: Sequel.function(:now)
|
|
8
|
+
timestamptz :updated_at
|
|
9
|
+
|
|
10
|
+
foreign_key :organization_id, :organizations, null: false, on_delete: :cascade
|
|
11
|
+
index :organization_id
|
|
12
|
+
|
|
13
|
+
text :name, null: false
|
|
14
|
+
unique [:organization_id, :name]
|
|
15
|
+
text :sql, null: false
|
|
16
|
+
|
|
17
|
+
foreign_key :created_by_id, :customers, null: true, on_delete: :set_null
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:backfill_job_service_integration_locks) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
foreign_key :service_integration_id, :service_integrations, null: false, on_delete: :cascade, unique: true
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
searchable = [
|
|
6
|
+
:backfill_jobs,
|
|
7
|
+
:customers,
|
|
8
|
+
:message_bodies,
|
|
9
|
+
:message_deliveries,
|
|
10
|
+
:organization_memberships,
|
|
11
|
+
:organization_database_migrations,
|
|
12
|
+
:organizations,
|
|
13
|
+
:roles,
|
|
14
|
+
:saved_queries,
|
|
15
|
+
:saved_views,
|
|
16
|
+
:service_integrations,
|
|
17
|
+
:subscriptions,
|
|
18
|
+
:sync_targets,
|
|
19
|
+
:webhook_subscription_deliveries,
|
|
20
|
+
:webhook_subscriptions,
|
|
21
|
+
]
|
|
22
|
+
searchable.each do |tbl|
|
|
23
|
+
alter_table(tbl) do
|
|
24
|
+
add_column :text_search, :tsvector
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
alter_table(:oauth_sessions) do
|
|
6
|
+
add_column :token_json, :jsonb
|
|
7
|
+
drop_column :authorization_code
|
|
8
|
+
add_constraint(
|
|
9
|
+
:no_token_json_if_used,
|
|
10
|
+
"NOT (used_at IS NOT NULL AND token_json IS NOT NULL)",
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
down do
|
|
16
|
+
alter_table(:oauth_sessions) do
|
|
17
|
+
drop_column :token_json
|
|
18
|
+
add_column :authorization_code, :text
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/integration/auth_spec.rb
CHANGED
|
@@ -41,13 +41,13 @@ RSpec.describe "auth", :integration do
|
|
|
41
41
|
customer = Webhookdb::Fixtures.customer.admin.instance
|
|
42
42
|
auth_customer(customer)
|
|
43
43
|
|
|
44
|
-
resp = get("/
|
|
44
|
+
resp = get("/admin_api/v1/auth")
|
|
45
45
|
expect(resp).to party_status(200)
|
|
46
46
|
expect(resp).to party_response(match(hash_including(name: customer.name)))
|
|
47
47
|
|
|
48
48
|
customer.remove_role(Webhookdb::Role.admin_role)
|
|
49
49
|
|
|
50
|
-
resp = get("/
|
|
50
|
+
resp = get("/admin_api/v1/auth")
|
|
51
51
|
expect(resp).to party_status(401)
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sequel/text_searchable"
|
|
4
|
+
|
|
5
|
+
module Sequel::Plugins::TextSearchable
|
|
6
|
+
DEFAULT_OPTIONS = {
|
|
7
|
+
column: :text_search,
|
|
8
|
+
search_options: {
|
|
9
|
+
to_tsquery: :websearch,
|
|
10
|
+
language: "english",
|
|
11
|
+
rank: true,
|
|
12
|
+
},
|
|
13
|
+
terms: nil,
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
def self.apply(model, *)
|
|
17
|
+
raise "The :dirty plugin must be loaded first. Use `plugin :dirty`." if
|
|
18
|
+
!model.plugins.include?(Sequel::Plugins::Dirty) ||
|
|
19
|
+
model.plugins.index(Sequel::Plugins::Dirty) > model.plugins.index(self)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.configure(model, opts=DEFAULT_OPTIONS)
|
|
23
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
|
24
|
+
model.text_search_column = opts[:column]
|
|
25
|
+
model.text_search_options = opts[:search_options]
|
|
26
|
+
model.text_search_terms = opts[:terms]
|
|
27
|
+
SequelTextSearchable.searchable_models << model
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module DatasetMethods
|
|
31
|
+
def text_search(q, opts={})
|
|
32
|
+
full_opts = self.model.text_search_options.merge(tsvector: true).merge(opts)
|
|
33
|
+
return self.full_text_search(self.model.text_search_column, q, **full_opts)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module ClassMethods
|
|
38
|
+
attr_accessor :text_search_column, :text_search_options, :text_search_terms
|
|
39
|
+
|
|
40
|
+
def text_search_language = self.text_search_options.fetch(:language)
|
|
41
|
+
|
|
42
|
+
def text_search_reindex_all
|
|
43
|
+
did = 0
|
|
44
|
+
self.dataset.paged_each do |m|
|
|
45
|
+
m.text_search_reindex
|
|
46
|
+
did += 1
|
|
47
|
+
end
|
|
48
|
+
return did
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def text_search_reindex_model(model_pk)
|
|
52
|
+
m = self.with_pk!(model_pk)
|
|
53
|
+
m.text_search_reindex
|
|
54
|
+
return m
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def text_search_columns_and_ranks
|
|
58
|
+
raise NotImplementedError, "#{self.name} must implement text_search_terms" if
|
|
59
|
+
self.text_search_terms.nil?
|
|
60
|
+
return self.text_search_terms.map do |t|
|
|
61
|
+
if t.is_a?(Array)
|
|
62
|
+
col, rank = t
|
|
63
|
+
elsif t.is_a?(Hash)
|
|
64
|
+
col, rank = t.to_a.first
|
|
65
|
+
else
|
|
66
|
+
col = t
|
|
67
|
+
rank = nil
|
|
68
|
+
end
|
|
69
|
+
[col, rank]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
module InstanceMethods
|
|
75
|
+
def after_create
|
|
76
|
+
super
|
|
77
|
+
self._run_after_model_hook
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def after_update
|
|
81
|
+
super
|
|
82
|
+
if self.class.text_search_terms.nil?
|
|
83
|
+
# If the instance implements a custom text_search_terms, we have to always call it,
|
|
84
|
+
# since we can't otherwise know if relevant values have changed.
|
|
85
|
+
self._run_after_model_hook
|
|
86
|
+
return
|
|
87
|
+
end
|
|
88
|
+
has_changes = self.class.text_search_columns_and_ranks.any? { |col, _rank| self.previous_changes.include?(col) }
|
|
89
|
+
self._run_after_model_hook if has_changes
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def _run_after_model_hook
|
|
93
|
+
if SequelTextSearchable.index_mode == :async
|
|
94
|
+
# We must refetch the model to index since it happens on another thread.
|
|
95
|
+
SequelTextSearchable.threadpool.post do
|
|
96
|
+
self.model.text_search_reindex_model(self.pk)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
elsif SequelTextSearchable.index_mode == :sync
|
|
100
|
+
self.text_search_reindex
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Return the values used for the tsvector value.
|
|
105
|
+
#
|
|
106
|
+
# In general this should include relevant text fields (like name and descriptions)
|
|
107
|
+
# on the receiver and related objects.
|
|
108
|
+
#
|
|
109
|
+
# Each value in the array can be one of the following:
|
|
110
|
+
#
|
|
111
|
+
# - nil: skipped
|
|
112
|
+
# - str like 'value': Used in `to_tsvector('value')`.
|
|
113
|
+
# - tuple[str, str] like ('value, 'B'): Used in `setweight(to_tsvector('value'), 'B')
|
|
114
|
+
# - has a text_search_values_for_related' method: All of these are included in the returned list.
|
|
115
|
+
# Useful for adding all of a parent relation's fields to related components,
|
|
116
|
+
# while the parent may need a more complex text_search_values.
|
|
117
|
+
# - has a 'text_search_values' method: All of these are included in the returned list.
|
|
118
|
+
def text_search_terms
|
|
119
|
+
raise NotImplementedError, "#{self.class.name} must implement text_search_terms" if
|
|
120
|
+
self.model.text_search_terms.nil?
|
|
121
|
+
return self.model.text_search_columns_and_ranks.map do |col, rank|
|
|
122
|
+
val = self.send(col)
|
|
123
|
+
rank ? [val, rank] : val
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def text_search_reindex
|
|
128
|
+
got_terms = self.text_search_terms
|
|
129
|
+
return if got_terms.empty?
|
|
130
|
+
terms = got_terms.flat_map { |t| _text_search_term_to_col_and_rank(t) }
|
|
131
|
+
exprs = terms.filter_map do |(col, rank)|
|
|
132
|
+
col = Sequel.function(:coalesce, col, "")
|
|
133
|
+
expr = Sequel.function(:to_tsvector, self.model.text_search_language, col)
|
|
134
|
+
expr = Sequel.function(:setweight, expr, rank) if rank
|
|
135
|
+
expr
|
|
136
|
+
end
|
|
137
|
+
full_expr = Sequel.join(exprs)
|
|
138
|
+
self.this.update(self.model.text_search_column => full_expr)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private def _text_search_term_to_col_and_rank(t, norank: false)
|
|
142
|
+
return nil if t.nil?
|
|
143
|
+
if t.is_a?(Array)
|
|
144
|
+
c, r = t
|
|
145
|
+
r = nil if norank
|
|
146
|
+
return [[c, r]]
|
|
147
|
+
end
|
|
148
|
+
if t.is_a?(Hash)
|
|
149
|
+
c, r = t.to_a.first
|
|
150
|
+
r = nil if norank
|
|
151
|
+
return [[c, r]]
|
|
152
|
+
end
|
|
153
|
+
related_cols_ranks = if t.respond_to?(:text_search_terms_for_related)
|
|
154
|
+
t.text_search_terms_for_related
|
|
155
|
+
elsif t.respond_to?(:text_search_terms)
|
|
156
|
+
t.text_search_terms
|
|
157
|
+
else
|
|
158
|
+
return [[t, nil]]
|
|
159
|
+
end
|
|
160
|
+
return related_cols_ranks.flat_map do |relterm|
|
|
161
|
+
_text_search_term_to_col_and_rank(relterm, norank: true)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|