21
1
Fork 0
mirror of https://github.com/Evolix/chexpire.git synced 2024-04-26 05:50:50 +02:00

Manual expiray date support for unsupported domain

Validation is made when leaving the input field with an ajax call on the
/checks/support.json path. JSON response include the normalized domain
name (more TODO) and the supported status.
UI is updated with Javascript accordingly to this response.

Closes #62
This commit is contained in:
Colin Darie 2018-08-29 16:42:00 +02:00
parent ef1229d900
commit 09be8a38c2
No known key found for this signature in database
GPG key ID: 4FB865FDBCA4BCC4
12 changed files with 165 additions and 29 deletions

View file

@ -3,7 +3,7 @@
class ChecksController < ApplicationController
before_action :authenticate_user!
before_action :set_check, except: [:index, :new, :create]
before_action :set_check, except: [:index, :new, :create, :supports]
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index
@ -65,6 +65,11 @@ class ChecksController < ApplicationController
redirect_to checks_path
end
def supports
@check = Check.new(new_check_params)
authorize @check
end
private
def set_check
@ -81,7 +86,7 @@ class ChecksController < ApplicationController
end
def check_params(*others)
params.require(:check).permit(:domain, :domain_created_at, :comment, :vendor, *others,
params.require(:check).permit(:domain, :domain_expires_at, :comment, :vendor, *others,
notifications_attributes: [:id, :channel, :recipient, :interval])
end

View file

@ -0,0 +1,57 @@
// Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
// License: GNU AGPL-3+ (see full text in LICENSE file)
function checkValidationInitialize() {
const element = document.getElementById("check_domain");
if (element && element.dataset.kind == "domain") {
addEventSupportListener(element);
}
}
function addEventSupportListener(element) {
element.addEventListener("blur", event => {
const request = $.ajax("/checks/supports.json", {
method: "post",
dataType: "json",
data: {
check: {
domain: event.target.value,
kind: element.dataset.kind,
}
}
})
request.done(response => {
const { supported } = response.check;
toggleUnsupportedContainers(supported);
setFocus(supported);
// set normalized domain
element.value = response.check.domain;
});
});
}
function toggleUnsupportedContainers(supported) {
const containerClass = supported ? "d-none" : "d-block";
document.getElementById("check_domain_expires_at_container").className = containerClass;
const domainHint = document.getElementById("check_domain_unsupported_container");
domainHint.classList.remove("d-none");
domainHint.classList.remove("d-block");
domainHint.classList.add(containerClass);
}
function setFocus(supported) {
if (supported) {
return;
}
document.getElementById("check_domain_expires_at").focus();
}
export default checkValidationInitialize;

View file

@ -20,9 +20,13 @@ import 'bootstrap/js/dist/tooltip';
import '../scss';
import checkValidationInitialize from '../components/check_validation';
Rails.start()
Turbolinks.start()
document.addEventListener("turbolinks:load", () => {
$('[data-toggle="tooltip"]').tooltip();
checkValidationInitialize();
});

View file

@ -51,6 +51,7 @@ class Check < ApplicationRecord
validates :domain, presence: true
validate :domain_created_at_past
validate :domain_updated_at_past
validates :domain_expires_at, presence: true, unless: :supported?
validates :comment, length: { maximum: 255 }
validates :vendor, length: { maximum: 255 }

View file

@ -20,6 +20,10 @@ class CheckPolicy < ApplicationPolicy
owner?
end
def supports?
new?
end
private
def owner?

View file

@ -3,13 +3,34 @@
<%= simple_form_for(check) do |f| %>
<%= f.input :domain,
autofocus: true,
input_html: { autocapitalize: :none, autocorrect: :off },
label: t(".#{check.kind || "generic" }.domain") %>
input_html: { autocapitalize: :none, autocorrect: :off, data: { kind: check.kind } },
label: t(".#{check.kind || "generic" }.domain"),
hint: t(".#{check.kind || "generic" }.unsupported"),
hint_html: {
id: "check_domain_unsupported_container",
class: "#{check.supported? && 'd-none'}",
}
%>
<% if check.new_record? %>
<%= f.input :kind, as: check.kind.present? ? :hidden : :radio_buttons, collection: Check.kinds.keys %>
<% end %>
<div id="check_domain_expires_at_container" class="<%= check.supported? ? "d-none" : "d-block" %>">
<%= f.input :domain_expires_at,
required: true,
input_html: {
type: :string,
value: check.domain_expires_at&.to_date,
# min: Date.yesterday,
# max: 10.years.from_now.end_of_year.to_date
},
as: :string,
placeholder: t(".domain_expires_at_placeholder"),
hint: t(".domain_expires_at_hint")
%>
</div>
<%= f.input :comment %>
<%= f.input :vendor %>

View file

@ -0,0 +1,7 @@
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
# License: GNU AGPL-3+ (see full text in LICENSE file)
json.check do
json.supported @check.supported?
json.domain normalize_domain(@check.domain)
end

View file

@ -72,17 +72,17 @@ en:
sign_in: "Log in"
sign_out: "Log out"
profile: "Profile"
home_header:
home_header:
welcome: "Chexpire"
intro: "Never forget to renew a domain name or SSL certificate."
beta_banner:
beta_banner:
beta_info: "Chexpire is in \"beta\" release: only few TLD (.com/.net/.org/.fr) are verified for domain name checks and TLS 1.2 is not supported for SSL checks."
issue_link: "Please report issues."
pages:
home:
why: "Why Chexpire?"
why: "Why Chexpire?"
description: "Chexpire is a Free Software (AGPLv3 license) to manage the expiration of domain names and SSL certificates. It is primarily an ergonomic web interface that allows you easily to add new domain names/SSL certificates to monitor, and custom/unlimited notifications to be notified before expiration."
centralization: "Centralize all your expiry dates"
centralization-details: "Do you have domain names at different registrars? many Let's Encrypt SSL certificates with automatic renewal? You will enjoy everything centralized in a web interface: adding a domain name/SSL certificate in two clicks, sorted list, search bar etc."
@ -119,6 +119,12 @@ en:
domain: Domain
domain:
domain: Domain name
unsupported: |
This top-level domain isn't currently automatically supported.
You'll have to fill and maintain yourself the expiry date.
domain_expires_at_hint: |
Fill the expiry date in YYYY-MM-DD format.
domain_expires_at_placeholder: YYYY-MM-DD.
ssl:
domain: Hostname
notifications_hint: |

View file

@ -11,6 +11,7 @@ fr:
kind: Type
domain_created_at: "Date de création"
domain_updated_at: "Date de modification"
domain_expires_at: "Date d'expiration"
notification:
interval: Délai
recipient: Destinataire
@ -104,17 +105,17 @@ fr:
sign_in: "Connexion"
sign_out: "Déconnexion"
profile: "Profil"
home_header:
home_header:
welcome: "Chexpire"
intro: "vous n'oublierez plus jamais de renouveler un nom de domaine ou un certificat SSL."
beta_banner:
beta_banner:
beta_info: "Chexpire est en version \"beta\" : seuls certains TLD (.com/.net/.org/.fr) sont vérifiés pour les noms de domaine et TLS 1.2 n'est pas supporté pour les vérifications SSL."
issue_link: "Merci de nous reporter bugs et suggestions."
pages:
home:
why: "Pourquoi Chexpire ?"
why: "Pourquoi Chexpire ?"
description: "Chexpire est un Logiciel Libre (licence AGPLv3) permettant de gérer l'expiration de noms de domaine et de certificats SSL. C'est avant tout une interface web ergonomique permettant d'ajouter simplement de nouveaux noms de domaine/certificats SSL à surveiller, et des notifications sur mesure et illimitées pour être averti·e avant expiration."
centralization: "Centralisez toutes vos dates d'expiration"
centralization-details: "Vous avez des noms de domaine chez différents registrars ? de nombreux certificats SSL Let's Encrypt en renouvellement automatique ? Vous allez apprécier de tout centraliser simplement dans une interface web : ajout d'un nom de domaine/certificat SSL en deux clics, liste récapitulative triée, barre de recherche etc."
@ -151,6 +152,12 @@ fr:
domain: Domaine
domain:
domain: Nom de domaine
unsupported: |
Cette extension n'est pas supportée automatiquement actuellement.
Vous devrez saisir et maintenir vous-même sa date d'expiration.
domain_expires_at_hint: |
Renseignez la date d'expiration au format AAAA-MM-JJ.
domain_expires_at_placeholder: AAAA-MM-JJ
ssl:
domain: Nom d'hôte
notifications_hint: |

View file

@ -1,10 +1,29 @@
# Copyright (C) 2018 Colin Darie <colin@darie.eu>, 2018 Evolix <info@evolix.fr>
# License: GNU AGPL-3+ (see full text in LICENSE file)
# In order to update the route map below,
# run `bundle exec annotate -r` after modifying this file
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :checks, except: [:show] do
resources :notifications, only: [:destroy]
collection do
post :supports, format: :json
end
end
devise_for :users
root to: "pages#home"
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
end
# == Route Map
#
# Prefix Verb URI Pattern Controller#Action
# check_notification DELETE /checks/:check_id/notifications/:id(.:format) notifications#destroy
# supports_checks POST /checks/supports(.:format) checks#supports
# checks GET /checks(.:format) checks#index
# POST /checks(.:format) checks#create
# new_check GET /checks/new(.:format) checks#new
@ -37,25 +56,10 @@
# rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
# update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
# rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create
#
#
# Routes for LetterOpenerWeb::Engine:
# clear_letters DELETE /clear(.:format) letter_opener_web/letters#clear
# delete_letter DELETE /:id(.:format) letter_opener_web/letters#destroy
# letters GET / letter_opener_web/letters#index
# letter GET /:id(/:style)(.:format) letter_opener_web/letters#show
# GET /:id/attachments/:file(.:format) letter_opener_web/letters#attachment
# In order to update the route map above,
# run `bundle exec annotate -r` after modifying this file
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :checks, except: [:show] do
resources :notifications, only: [:destroy]
end
devise_for :users
root to: "pages#home"
mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?
end

View file

@ -8,7 +8,7 @@ if Rails.env.development?
# same name.
Annotate.set_defaults(
'routes' => 'before',
'position_in_routes' => 'before',
'position_in_routes' => 'after',
'position_in_class' => 'before',
'position_in_test' => 'before',
'position_in_fixture' => 'before',

View file

@ -25,6 +25,26 @@ class ChecksTest < ApplicationSystemTestCase
fill_and_valid_new_check
end
test "create a manual domain check" do
visit new_check_path(kind: :domain)
domain = "unsupported.wxyz"
fill_in("check[domain]", with: domain)
page.find("body").click # simulate blur
fill_in("check[domain_expires_at]", with: "2022-04-05")
click_button
assert_equal checks_path, page.current_path
assert page.has_css?(".alert-success")
assert page.has_content?(domain)
check = Check.last
assert_equal Date.new(2022, 4, 5), check.domain_expires_at
end
test "create a predefined ssl check" do
visit new_check_path(kind: :ssl)