forked from GithubBackups/healthchecks
THe LINE Notify integration uses OAuth2 flow.
This commit is contained in:
parent
25a8ec6bd9
commit
4f53325730
@ -136,6 +136,8 @@ Configurations settings loaded from environment variables:
|
|||||||
| MATRIX_ACCESS_TOKEN | `None`
|
| MATRIX_ACCESS_TOKEN | `None`
|
||||||
| APPRISE_ENABLED | `"False"`
|
| APPRISE_ENABLED | `"False"`
|
||||||
| SHELL_ENABLED | `"False"`
|
| SHELL_ENABLED | `"False"`
|
||||||
|
| LINENOTIFY_CLIENT_ID | `None`
|
||||||
|
| LINENOTIFY_CLIENT_SECRET | `None`
|
||||||
|
|
||||||
|
|
||||||
Some useful settings keys to override are:
|
Some useful settings keys to override are:
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
from hc.api.models import Channel
|
from django.test.utils import override_settings
|
||||||
from hc.test import BaseTestCase
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(LINENOTIFY_CLIENT_ID="t1", LINENOTIFY_CLIENT_SECRET="s1")
|
||||||
class AddLineNotifyTestCase(BaseTestCase):
|
class AddLineNotifyTestCase(BaseTestCase):
|
||||||
url = "/integrations/add_linenotify/"
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(AddLineNotifyTestCase, self).setUp()
|
super(AddLineNotifyTestCase, self).setUp()
|
||||||
self.url = "/projects/%s/add_linenotify/" % self.project.code
|
self.url = "/projects/%s/add_linenotify/" % self.project.code
|
||||||
@ -12,32 +11,17 @@ class AddLineNotifyTestCase(BaseTestCase):
|
|||||||
def test_instructions_work(self):
|
def test_instructions_work(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.get(self.url)
|
r = self.client.get(self.url)
|
||||||
self.assertContains(r, "LineNotify")
|
self.assertContains(r, "notify-bot.line.me/oauth/authorize", status_code=200)
|
||||||
|
self.assertContains(r, "Connect LINE Notify")
|
||||||
|
|
||||||
def test_it_works(self):
|
# There should now be a key in session
|
||||||
form = {"token": "helloworld"}
|
self.assertTrue("add_linenotify" in self.client.session)
|
||||||
|
|
||||||
|
@override_settings(LINENOTIFY_CLIENT_ID=None)
|
||||||
|
def test_it_requires_client_id(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.post(self.url, form)
|
r = self.client.get(self.url)
|
||||||
self.assertRedirects(r, self.channels_url)
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
c = Channel.objects.get()
|
|
||||||
self.assertEqual(c.kind, "linenotify")
|
|
||||||
self.assertEqual(c.value, "helloworld")
|
|
||||||
self.assertEqual(c.project, self.project)
|
|
||||||
|
|
||||||
def test_it_handles_json_linenotify_value(self):
|
|
||||||
c = Channel(kind="linenotify", value="foo123")
|
|
||||||
self.assertEqual(c.linenotify_token, "foo123")
|
|
||||||
|
|
||||||
def test_it_save_token(self):
|
|
||||||
form = {"token": "foo123"}
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
self.client.post(self.url, form)
|
|
||||||
|
|
||||||
c = Channel.objects.get()
|
|
||||||
self.assertEqual(c.value, "foo123")
|
|
||||||
|
|
||||||
def test_it_requires_rw_access(self):
|
def test_it_requires_rw_access(self):
|
||||||
self.bobs_membership.rw = False
|
self.bobs_membership.rw = False
|
||||||
|
81
hc/front/tests/test_add_linenotify_complete.py
Normal file
81
hc/front/tests/test_add_linenotify_complete.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
from hc.api.models import Channel
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(LINENOTIFY_CLIENT_ID="t1", LINENOTIFY_CLIENT_SECRET="s1")
|
||||||
|
class AddLineNotifyCompleteTestCase(BaseTestCase):
|
||||||
|
url = "/integrations/add_linenotify/"
|
||||||
|
|
||||||
|
@patch("hc.front.views.requests")
|
||||||
|
def test_it_handles_oauth_response(self, mock_requests):
|
||||||
|
session = self.client.session
|
||||||
|
session["add_linenotify"] = ("foo", str(self.project.code))
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
mock_requests.post.return_value.json.return_value = {
|
||||||
|
"status": 200,
|
||||||
|
"access_token": "test-token",
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_requests.get.return_value.json.return_value = {"target": "Alice"}
|
||||||
|
|
||||||
|
url = self.url + "?code=12345678&state=foo"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url, follow=True)
|
||||||
|
self.assertRedirects(r, self.channels_url)
|
||||||
|
self.assertContains(r, "The LINE Notify integration has been added!")
|
||||||
|
|
||||||
|
ch = Channel.objects.get()
|
||||||
|
self.assertEqual(ch.value, "test-token")
|
||||||
|
self.assertEqual(ch.name, "Alice")
|
||||||
|
self.assertEqual(ch.project, self.project)
|
||||||
|
|
||||||
|
# Session should now be clean
|
||||||
|
self.assertFalse("add_linenotify" in self.client.session)
|
||||||
|
|
||||||
|
def test_it_avoids_csrf(self):
|
||||||
|
session = self.client.session
|
||||||
|
session["add_linenotify"] = ("foo", str(self.project.code))
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
url = self.url + "?code=12345678&state=bar"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
|
def test_it_handles_denial(self):
|
||||||
|
session = self.client.session
|
||||||
|
session["add_linenotify"] = ("foo", str(self.project.code))
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(self.url + "?error=access_denied&state=foo", follow=True)
|
||||||
|
self.assertRedirects(r, self.channels_url)
|
||||||
|
self.assertContains(r, "LINE Notify setup was cancelled")
|
||||||
|
|
||||||
|
self.assertEqual(Channel.objects.count(), 0)
|
||||||
|
|
||||||
|
# Session should now be clean
|
||||||
|
self.assertFalse("add_linenotify" in self.client.session)
|
||||||
|
|
||||||
|
@override_settings(LINENOTIFY_CLIENT_ID=None)
|
||||||
|
def test_it_requires_client_id(self):
|
||||||
|
url = self.url + "?code=12345678&state=bar"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
|
def test_it_requires_rw_access(self):
|
||||||
|
self.bobs_membership.rw = False
|
||||||
|
self.bobs_membership.save()
|
||||||
|
|
||||||
|
url = self.url + "?code=12345678&state=bar"
|
||||||
|
self.client.login(username="bob@example.org", password="password")
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 403)
|
@ -37,7 +37,7 @@ class AddPushbulletTestCase(BaseTestCase):
|
|||||||
|
|
||||||
def test_it_avoids_csrf(self):
|
def test_it_avoids_csrf(self):
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session["pushbullet"] = ("foo", str(self.project.code))
|
session["add_pushbullet"] = ("foo", str(self.project.code))
|
||||||
session.save()
|
session.save()
|
||||||
|
|
||||||
url = self.url + "?code=12345678&state=bar&project=%s" % self.project.code
|
url = self.url + "?code=12345678&state=bar&project=%s" % self.project.code
|
||||||
|
@ -30,6 +30,7 @@ channel_urls = [
|
|||||||
name="hc-add-pushbullet-complete",
|
name="hc-add-pushbullet-complete",
|
||||||
),
|
),
|
||||||
path("add_discord/", views.add_discord_complete, name="hc-add-discord-complete"),
|
path("add_discord/", views.add_discord_complete, name="hc-add-discord-complete"),
|
||||||
|
path("add_linenotify/", views.add_linenotify_complete),
|
||||||
path("add_pushover/", views.pushover_help, name="hc-pushover-help"),
|
path("add_pushover/", views.pushover_help, name="hc-pushover-help"),
|
||||||
path("telegram/", views.telegram_help, name="hc-telegram-help"),
|
path("telegram/", views.telegram_help, name="hc-telegram-help"),
|
||||||
path("telegram/bot/", views.telegram_bot, name="hc-telegram-webhook"),
|
path("telegram/bot/", views.telegram_bot, name="hc-telegram-webhook"),
|
||||||
|
@ -290,7 +290,9 @@ def index(request):
|
|||||||
"check": check,
|
"check": check,
|
||||||
"ping_url": check.url(),
|
"ping_url": check.url(),
|
||||||
"enable_apprise": settings.APPRISE_ENABLED is True,
|
"enable_apprise": settings.APPRISE_ENABLED is True,
|
||||||
|
"enable_call": settings.TWILIO_AUTH is not None,
|
||||||
"enable_discord": settings.DISCORD_CLIENT_ID is not None,
|
"enable_discord": settings.DISCORD_CLIENT_ID is not None,
|
||||||
|
"enable_linenotify": settings.LINENOTIFY_CLIENT_ID is not None,
|
||||||
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
|
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
|
||||||
"enable_pdc": settings.PD_VENDOR_KEY is not None,
|
"enable_pdc": settings.PD_VENDOR_KEY is not None,
|
||||||
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
|
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
|
||||||
@ -298,7 +300,6 @@ def index(request):
|
|||||||
"enable_shell": settings.SHELL_ENABLED is True,
|
"enable_shell": settings.SHELL_ENABLED is True,
|
||||||
"enable_slack_btn": settings.SLACK_CLIENT_ID is not None,
|
"enable_slack_btn": settings.SLACK_CLIENT_ID is not None,
|
||||||
"enable_sms": settings.TWILIO_AUTH is not None,
|
"enable_sms": settings.TWILIO_AUTH is not None,
|
||||||
"enable_call": settings.TWILIO_AUTH is not None,
|
|
||||||
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
|
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
|
||||||
"enable_trello": settings.TRELLO_APP_KEY is not None,
|
"enable_trello": settings.TRELLO_APP_KEY is not None,
|
||||||
"enable_whatsapp": settings.TWILIO_USE_WHATSAPP,
|
"enable_whatsapp": settings.TWILIO_USE_WHATSAPP,
|
||||||
@ -738,7 +739,9 @@ def channels(request, code):
|
|||||||
"profile": project.owner_profile,
|
"profile": project.owner_profile,
|
||||||
"channels": channels,
|
"channels": channels,
|
||||||
"enable_apprise": settings.APPRISE_ENABLED is True,
|
"enable_apprise": settings.APPRISE_ENABLED is True,
|
||||||
|
"enable_call": settings.TWILIO_AUTH is not None,
|
||||||
"enable_discord": settings.DISCORD_CLIENT_ID is not None,
|
"enable_discord": settings.DISCORD_CLIENT_ID is not None,
|
||||||
|
"enable_linenotify": settings.LINENOTIFY_CLIENT_ID is not None,
|
||||||
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
|
"enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None,
|
||||||
"enable_pdc": settings.PD_VENDOR_KEY is not None,
|
"enable_pdc": settings.PD_VENDOR_KEY is not None,
|
||||||
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
|
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
|
||||||
@ -746,7 +749,6 @@ def channels(request, code):
|
|||||||
"enable_shell": settings.SHELL_ENABLED is True,
|
"enable_shell": settings.SHELL_ENABLED is True,
|
||||||
"enable_slack_btn": settings.SLACK_CLIENT_ID is not None,
|
"enable_slack_btn": settings.SLACK_CLIENT_ID is not None,
|
||||||
"enable_sms": settings.TWILIO_AUTH is not None,
|
"enable_sms": settings.TWILIO_AUTH is not None,
|
||||||
"enable_call": settings.TWILIO_AUTH is not None,
|
|
||||||
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
|
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
|
||||||
"enable_trello": settings.TRELLO_APP_KEY is not None,
|
"enable_trello": settings.TRELLO_APP_KEY is not None,
|
||||||
"enable_whatsapp": settings.TWILIO_USE_WHATSAPP,
|
"enable_whatsapp": settings.TWILIO_USE_WHATSAPP,
|
||||||
@ -1819,21 +1821,82 @@ def add_spike(request, code):
|
|||||||
return render(request, "integrations/add_spike.html", ctx)
|
return render(request, "integrations/add_spike.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
|
@require_setting("LINENOTIFY_CLIENT_ID")
|
||||||
@login_required
|
@login_required
|
||||||
def add_linenotify(request, code):
|
def add_linenotify(request, code):
|
||||||
project = _get_rw_project_for_user(request, code)
|
project = _get_rw_project_for_user(request, code)
|
||||||
|
redirect_uri = settings.SITE_ROOT + reverse(add_linenotify_complete)
|
||||||
|
|
||||||
if request.method == "POST":
|
state = token_urlsafe()
|
||||||
form = forms.AddLineNotifyForm(request.POST)
|
authorize_url = " https://notify-bot.line.me/oauth/authorize?" + urlencode(
|
||||||
if form.is_valid():
|
{
|
||||||
channel = Channel(project=project, kind="linenotify")
|
"client_id": settings.LINENOTIFY_CLIENT_ID,
|
||||||
channel.value = form.cleaned_data["token"]
|
"redirect_uri": redirect_uri,
|
||||||
channel.save()
|
"response_type": "code",
|
||||||
|
"state": state,
|
||||||
|
"scope": "notify",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
channel.assign_all_checks()
|
ctx = {
|
||||||
return redirect("hc-p-channels", project.code)
|
"page": "channels",
|
||||||
else:
|
"project": project,
|
||||||
form = forms.AddLineNotifyForm()
|
"authorize_url": authorize_url,
|
||||||
|
}
|
||||||
|
|
||||||
ctx = {"page": "channels", "project": project, "form": form}
|
request.session["add_linenotify"] = (state, str(project.code))
|
||||||
return render(request, "integrations/add_linenotify.html", ctx)
|
return render(request, "integrations/add_linenotify.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
|
@require_setting("LINENOTIFY_CLIENT_ID")
|
||||||
|
@login_required
|
||||||
|
def add_linenotify_complete(request):
|
||||||
|
if "add_linenotify" not in request.session:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
state, code = request.session.pop("add_linenotify")
|
||||||
|
if request.GET.get("state") != state:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
project = _get_rw_project_for_user(request, code)
|
||||||
|
if request.GET.get("error") == "access_denied":
|
||||||
|
messages.warning(request, "LINE Notify setup was cancelled.")
|
||||||
|
return redirect("hc-p-channels", project.code)
|
||||||
|
|
||||||
|
# Exchange code for access token
|
||||||
|
redirect_uri = settings.SITE_ROOT + reverse(add_linenotify_complete)
|
||||||
|
result = requests.post(
|
||||||
|
"https://notify-bot.line.me/oauth/token",
|
||||||
|
{
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"code": request.GET.get("code"),
|
||||||
|
"redirect_uri": redirect_uri,
|
||||||
|
"client_id": settings.LINENOTIFY_CLIENT_ID,
|
||||||
|
"client_secret": settings.LINENOTIFY_CLIENT_SECRET,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
doc = result.json()
|
||||||
|
if doc.get("status") != 200:
|
||||||
|
messages.warning(request, "Something went wrong.")
|
||||||
|
return redirect("hc-p-channels", project.code)
|
||||||
|
|
||||||
|
# Fetch notification target's name, will use it as channel name:
|
||||||
|
token = doc["access_token"]
|
||||||
|
result = requests.get(
|
||||||
|
"https://notify-api.line.me/api/status",
|
||||||
|
headers={"Authorization": "Bearer %s" % token},
|
||||||
|
)
|
||||||
|
doc = result.json()
|
||||||
|
|
||||||
|
channel = Channel(kind="linenotify", project=project)
|
||||||
|
channel.name = doc.get("target")
|
||||||
|
channel.value = token
|
||||||
|
channel.save()
|
||||||
|
channel.assign_all_checks()
|
||||||
|
messages.success(request, "The LINE Notify integration has been added!")
|
||||||
|
|
||||||
|
return redirect("hc-p-channels", project.code)
|
||||||
|
|
||||||
|
|
||||||
|
# Forks: add custom views after this line
|
||||||
|
@ -219,6 +219,9 @@ APPRISE_ENABLED = envbool("APPRISE_ENABLED", "False")
|
|||||||
# Local shell commands
|
# Local shell commands
|
||||||
SHELL_ENABLED = envbool("SHELL_ENABLED", "False")
|
SHELL_ENABLED = envbool("SHELL_ENABLED", "False")
|
||||||
|
|
||||||
|
# LINE Notify
|
||||||
|
LINENOTIFY_CLIENT_ID = os.getenv("LINENOTIFY_CLIENT_ID")
|
||||||
|
LINENOTIFY_CLIENT_SECRET = os.getenv("LINENOTIFY_CLIENT_SECRET")
|
||||||
|
|
||||||
if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
|
if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
|
||||||
from .local_settings import *
|
from .local_settings import *
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 253 KiB |
Binary file not shown.
Before Width: | Height: | Size: 121 KiB |
Binary file not shown.
Before Width: | Height: | Size: 79 KiB |
@ -230,6 +230,7 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if enable_linenotify %}
|
||||||
<li>
|
<li>
|
||||||
<img src="{% static 'img/integrations/linenotify.png' %}"
|
<img src="{% static 'img/integrations/linenotify.png' %}"
|
||||||
class="icon" alt="LINE Notify icon" />
|
class="icon" alt="LINE Notify icon" />
|
||||||
@ -238,6 +239,7 @@
|
|||||||
<p>Receive a notification on LINE when a check goes up or down.</p>
|
<p>Receive a notification on LINE when a check goes up or down.</p>
|
||||||
<a href="{% url 'hc-add-linenotify' project.code %}" class="btn btn-primary">Add Integration</a>
|
<a href="{% url 'hc-add-linenotify' project.code %}" class="btn btn-primary">Add Integration</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if enable_matrix %}
|
{% if enable_matrix %}
|
||||||
<li>
|
<li>
|
||||||
|
@ -435,12 +435,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if enable_linenotify %}
|
||||||
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
|
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
|
||||||
<div class="integration">
|
<div class="integration">
|
||||||
<img src="{% static 'img/integrations/linenotify.png' %}" class="icon" alt="" />
|
<img src="{% static 'img/integrations/linenotify.png' %}" class="icon" alt="" />
|
||||||
<h3>LINE Notify<br><small>Chat</small></h3>
|
<h3>LINE Notify<br><small>Chat</small></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if enable_matrix %}
|
{% if enable_matrix %}
|
||||||
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
|
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
|
||||||
|
@ -1,104 +1,30 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load humanize static hc_extras %}
|
{% load humanize static hc_extras %}
|
||||||
|
|
||||||
{% block title %}LineNotify Integration for {% site_name %}{% endblock %}
|
{% block title %}LINE Notify Integration for {{ site_name }}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h1>LINE Notify</h1>
|
<h1>LINE Notify</h1>
|
||||||
|
|
||||||
<p><a href="https://notify-bot.line.me/en/">LINE Notify</a>
|
<div class="jumbotron">
|
||||||
allows you to send notifications directly to your LINE chats.
|
<p>
|
||||||
You can set up {% site_name %} to post status updates directly to an
|
With the <a href="https://notify-bot.line.me/en/">LINE Notify</a>
|
||||||
appropriate LINE chat. </p>
|
integration, {{ site_name }} will send
|
||||||
|
a notification to your selected LINE chat when a check
|
||||||
|
goes <strong>up</strong> or <strong>down</strong>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post" class="text-center">
|
||||||
<h2>Setup Guide</h2>
|
{% csrf_token %}
|
||||||
|
<a href="{{ authorize_url }}" class="btn btn-lg btn-default">
|
||||||
<div class="row ai-step">
|
<img class="ai-icon" src="{% static 'img/integrations/linenotify.png' %}" alt="LINE Notify" />
|
||||||
<div class="col-sm-6">
|
Connect LINE Notify
|
||||||
<span class="step-no"></span>
|
</a>
|
||||||
<p>
|
</form>
|
||||||
Log into your LINE account,
|
</div>
|
||||||
go to <strong>My Page</strong> in the upper right corner.
|
|
||||||
In the <strong>My Page</strong>, scroll to the bottom of the page,
|
|
||||||
and click on <strong>Generate token</strong> button.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="marker-wrap">
|
|
||||||
<span style="left: 77%; top: 14%;" class="marker"></span>
|
|
||||||
<img
|
|
||||||
class="ai-guide-screenshot"
|
|
||||||
alt="Screenshot"
|
|
||||||
src="{% static 'img/integrations/setup_linenotify_1.png' %}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row ai-step">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<span class="step-no"></span>
|
|
||||||
<p>
|
|
||||||
It will pop up the <strong>Generate token</strong> Form.
|
|
||||||
Fill out the details, and click the button.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<img
|
|
||||||
class="ai-guide-screenshot"
|
|
||||||
alt="Screenshot"
|
|
||||||
src="{% static 'img/integrations/setup_linenotify_2.png' %}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row ai-step">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<span class="step-no"></span>
|
|
||||||
<p>
|
|
||||||
Copy the displayed <strong>Token</strong> and paste it
|
|
||||||
in the form below.
|
|
||||||
Save the integration, and it's done!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<img
|
|
||||||
class="ai-guide-screenshot"
|
|
||||||
alt="Screenshot"
|
|
||||||
src="{% static 'img/integrations/setup_linenotify_3.png' %}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Integration Settings</h2>
|
|
||||||
|
|
||||||
<form method="post" class="form-horizontal">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="form-group {{ form.value.css_classes }}">
|
|
||||||
<label for="api-token" class="col-sm-2 control-label">API Token</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
id="api-token"
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
name="token"
|
|
||||||
placeholder=""
|
|
||||||
value="{{ form.token.value|default:"" }}">
|
|
||||||
|
|
||||||
{% if form.token.errors %}
|
|
||||||
<div class="help-block">
|
|
||||||
{{ form.token.errors|join:"" }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
|
||||||
<button type="submit" class="btn btn-primary">Save Integration</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user