Merge pull request #10499 from mfo/US/email-check-with-levenshtein
tech: ETQ usager je souhaite une verification de mes mails plus efficace
This commit is contained in:
commit
03381c05b3
11 changed files with 767 additions and 7 deletions
1
Gemfile
1
Gemfile
|
@ -95,6 +95,7 @@ gem 'sidekiq'
|
|||
gem 'sidekiq-cron'
|
||||
gem 'skylight'
|
||||
gem 'spreadsheet_architect'
|
||||
gem 'string-similarity'
|
||||
gem 'strong_migrations' # lint database migrations
|
||||
gem 'sys-proctable'
|
||||
gem 'turbo-rails'
|
||||
|
|
|
@ -765,6 +765,7 @@ GEM
|
|||
activesupport (>= 5.2)
|
||||
sprockets (>= 3.0.0)
|
||||
stackprof (0.2.26)
|
||||
string-similarity (2.1.0)
|
||||
stringio (3.1.0)
|
||||
strong_migrations (1.8.0)
|
||||
activerecord (>= 5.2)
|
||||
|
@ -1013,6 +1014,7 @@ DEPENDENCIES
|
|||
spring
|
||||
spring-commands-rspec
|
||||
stackprof
|
||||
string-similarity
|
||||
strong_migrations
|
||||
sys-proctable
|
||||
timecop
|
||||
|
|
|
@ -32,7 +32,7 @@ class Dsfr::InputComponent < ApplicationComponent
|
|||
}.merge(input_group_error_class_names))
|
||||
}
|
||||
if email?
|
||||
opts[:data] = { controller: 'email-input' }
|
||||
opts[:data] = { controller: 'email-input', email_input_url_value: show_email_suggestions_path }
|
||||
end
|
||||
opts
|
||||
end
|
||||
|
|
5
app/controllers/email_checker_controller.rb
Normal file
5
app/controllers/email_checker_controller.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class EmailCheckerController < ApplicationController
|
||||
def show
|
||||
render json: EmailChecker.new.check(email: params[:email])
|
||||
end
|
||||
end
|
|
@ -1,18 +1,43 @@
|
|||
import { suggest } from 'email-butler';
|
||||
import { httpRequest } from '@utils';
|
||||
import { show, hide } from '@utils';
|
||||
import { ApplicationController } from './application_controller';
|
||||
|
||||
type checkEmailResponse = {
|
||||
success: boolean;
|
||||
email_suggestions: string[];
|
||||
};
|
||||
|
||||
export class EmailInputController extends ApplicationController {
|
||||
static targets = ['ariaRegion', 'suggestion', 'input'];
|
||||
|
||||
static values = {
|
||||
url: String
|
||||
};
|
||||
|
||||
declare readonly urlValue: string;
|
||||
|
||||
declare readonly ariaRegionTarget: HTMLElement;
|
||||
declare readonly suggestionTarget: HTMLElement;
|
||||
declare readonly inputTarget: HTMLInputElement;
|
||||
|
||||
checkEmail() {
|
||||
const suggestion = suggest(this.inputTarget.value);
|
||||
if (suggestion && suggestion.full) {
|
||||
this.suggestionTarget.innerHTML = suggestion.full;
|
||||
async checkEmail() {
|
||||
if (
|
||||
!this.inputTarget.value ||
|
||||
this.inputTarget.value.length < 5 ||
|
||||
!this.inputTarget.value.includes('@')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(this.urlValue, document.baseURI);
|
||||
url.searchParams.append('email', this.inputTarget.value);
|
||||
|
||||
const data: checkEmailResponse | null = await httpRequest(
|
||||
url.toString()
|
||||
).json();
|
||||
|
||||
if (data && data.email_suggestions && data.email_suggestions.length > 0) {
|
||||
this.suggestionTarget.innerHTML = data.email_suggestions[0];
|
||||
show(this.ariaRegionTarget);
|
||||
this.ariaRegionTarget.setAttribute('aria-live', 'assertive');
|
||||
}
|
||||
|
|
652
app/lib/email_checker.rb
Normal file
652
app/lib/email_checker.rb
Normal file
|
@ -0,0 +1,652 @@
|
|||
class EmailChecker
|
||||
# Extracted 100 most used domain on our users table [june 2024]
|
||||
# + all .gouv.fr domain on our users table
|
||||
# + all .ac-xxx on our users table
|
||||
KNOWN_DOMAINS = [
|
||||
'gmail.com',
|
||||
'hotmail.fr',
|
||||
'orange.fr',
|
||||
'yahoo.fr',
|
||||
'hotmail.com',
|
||||
'outlook.fr',
|
||||
'wanadoo.fr',
|
||||
'free.fr',
|
||||
'yahoo.com',
|
||||
'icloud.com',
|
||||
'laposte.net',
|
||||
'live.fr',
|
||||
'sfr.fr',
|
||||
'outlook.com',
|
||||
'neuf.fr',
|
||||
'aol.com',
|
||||
'bbox.fr',
|
||||
'msn.com',
|
||||
'me.com',
|
||||
'gmx.fr',
|
||||
'protonmail.com',
|
||||
'club-internet.fr',
|
||||
'live.com',
|
||||
'ymail.com',
|
||||
'ars.sante.fr',
|
||||
'mail.ru',
|
||||
'cegetel.net',
|
||||
'numericable.fr',
|
||||
'aliceadsl.fr',
|
||||
'comcast.net',
|
||||
'assurance-maladie.fr',
|
||||
'mac.com',
|
||||
'naver.com',
|
||||
'airbus.com',
|
||||
'justice.fr',
|
||||
'pole-emploi.fr',
|
||||
'educagri.fr',
|
||||
'aphp.fr',
|
||||
'netcourrier.com',
|
||||
'dbmail.com',
|
||||
'aol.fr',
|
||||
'qq.com',
|
||||
'hotmail.co.uk',
|
||||
'yahoo.co.uk',
|
||||
'proxima-mail.fr',
|
||||
'yahoo.com.br',
|
||||
'sciencespo.fr',
|
||||
'gmx.com',
|
||||
'etu.univ-st-etienne.fr',
|
||||
'yahoo.ca',
|
||||
'163.com',
|
||||
'francetravail.fr',
|
||||
'mail.pf',
|
||||
'nantesmetropole.fr',
|
||||
'hotmail.it',
|
||||
'sbcglobal.net',
|
||||
'noos.fr',
|
||||
'ird.fr',
|
||||
'safrangroup.com',
|
||||
'croix-rouge.fr',
|
||||
'eiffage.com',
|
||||
'veolia.com',
|
||||
'notaires.fr',
|
||||
'nordnet.fr',
|
||||
'videotron.ca',
|
||||
'paris.fr',
|
||||
'lilo.org',
|
||||
'mfr.asso.fr',
|
||||
'yopmail.com',
|
||||
'ukr.net',
|
||||
'onf.fr',
|
||||
'stellantis.com',
|
||||
'9online.fr',
|
||||
'atmp50.fr',
|
||||
'engie.com',
|
||||
'libertysurf.fr',
|
||||
'mailo.com',
|
||||
'auchan.fr',
|
||||
'verizon.net',
|
||||
'rocketmail.com',
|
||||
'mpsa.com',
|
||||
'entrepreneur.fr',
|
||||
'googlemail.com',
|
||||
'arcelormittal.com',
|
||||
'groupe-sos.org',
|
||||
'proton.me',
|
||||
'att.net',
|
||||
'pm.me',
|
||||
'orange.com',
|
||||
'abv.bg',
|
||||
'yahoo.es',
|
||||
'creditmutuel.fr',
|
||||
'yandex.ru',
|
||||
'essec.edu',
|
||||
'urssaf.fr',
|
||||
'bpifrance.fr',
|
||||
'uol.com.br',
|
||||
'suez.com',
|
||||
'univ-st-etienne.fr',
|
||||
'korian.fr',
|
||||
'developpement-durable.gouv.fr',
|
||||
'modernisation.gouv.fr',
|
||||
'social.gouv.fr',
|
||||
'emploi.gouv.fr',
|
||||
'agriculture.gouv.fr',
|
||||
'intradef.gouv.fr',
|
||||
'interieur.gouv.fr',
|
||||
'oise.gouv.fr',
|
||||
'direccte.gouv.fr',
|
||||
'culture.gouv.fr',
|
||||
'pas-de-calais.gouv.fr',
|
||||
'finances.gouv.fr',
|
||||
'drieets.gouv.fr',
|
||||
'drjscs.gouv.fr',
|
||||
'sg.social.gouv.fr',
|
||||
'martinique.pref.gouv.fr',
|
||||
'beta.gouv.fr',
|
||||
'dieccte.gouv.fr',
|
||||
'cotes-darmor.gouv.fr',
|
||||
'vosges.gouv.fr',
|
||||
'developppement-durable.gouv.fr',
|
||||
'mayenne.gouv.fr',
|
||||
'aviation-civile.gouv.fr',
|
||||
'data.gouv.fr',
|
||||
'recherche.gouv.fr',
|
||||
'sante.gouv.fr',
|
||||
'paris-idf.gouv.fr',
|
||||
'guyane.gouv.fr',
|
||||
'douane.finances.gouv.fr',
|
||||
'cget.gouv.fr',
|
||||
'herault.gouv.fr',
|
||||
'loire-atlantique.gouv.fr',
|
||||
'manche.gouv.fr',
|
||||
'seine-maritime.gouv.fr',
|
||||
'dgccrf.finances.gouv.fr',
|
||||
'tarn-et-garonne.gouv.fr',
|
||||
'dila.gouv.fr',
|
||||
'diplomatie.gouv.fr',
|
||||
'haut-rhin.gouv.fr',
|
||||
'nord.gouv.fr',
|
||||
'bouches-du-rhone.gouv.fr',
|
||||
'alpes-de-haute-provence.gouv.fr',
|
||||
'hautes-alpes.gouv.fr',
|
||||
'alpes-maritimes.gouv.fr',
|
||||
'var.gouv.fr',
|
||||
'vaucluse.gouv.fr',
|
||||
'rhone.gouv.fr',
|
||||
'occitanie.gouv.fr',
|
||||
'ille-et-vilaine.gouv.fr',
|
||||
'finistere.gouv.fr',
|
||||
'aisne.gouv.fr',
|
||||
'indre.gouv.fr',
|
||||
'yvelines.gouv.fr',
|
||||
'bas-rhin.gouv.fr',
|
||||
'landes.gouv.fr',
|
||||
'haute-marne.gouv.fr',
|
||||
'correze.gouv.fr',
|
||||
'val-doise.gouv.fr',
|
||||
'seine-et-marne.gouv.fr',
|
||||
'essonne.gouv.fr',
|
||||
'calvados.gouv.fr',
|
||||
'charente-maritime.gouv.fr',
|
||||
'corse-du-sud.gouv.fr',
|
||||
'gironde.gouv.fr',
|
||||
'haute-corse.gouv.fr',
|
||||
'morbihan.gouv.fr',
|
||||
'pyrenees-atlantiques.gouv.fr',
|
||||
'pyrenees-orientales.gouv.fr',
|
||||
'somme.gouv.fr',
|
||||
'vendee.gouv.fr',
|
||||
'dgtresor.gouv.fr',
|
||||
'marne.gouv.fr',
|
||||
'auvergne-rhone-alpes.gouv.fr',
|
||||
'meurthe-et-moselle.gouv.fr',
|
||||
'pm.gouv.fr',
|
||||
'oncfs.gouv.fr',
|
||||
'orne.gouv.fr',
|
||||
'charente.gouv.fr',
|
||||
'travail.gouv.fr',
|
||||
'gard.gouv.fr',
|
||||
'maine-et-loire.gouv.fr',
|
||||
'moselle.gouv.fr',
|
||||
'outre-mer.gouv.fr',
|
||||
'jscs.gouv.fr',
|
||||
'haute-garonne.gouv.fr',
|
||||
'vienne.gouv.fr',
|
||||
'dordogne.gouv.fr',
|
||||
'eure.gouv.fr',
|
||||
'meuse.gouv.fr',
|
||||
'savoie.gouv.fr',
|
||||
'doubs.gouv.fr',
|
||||
'bfc.gouv.fr',
|
||||
'education.gouv.fr',
|
||||
'ariege.gouv.fr',
|
||||
'normandie.gouv.fr',
|
||||
'gendarmerie.interieur.gouv.fr',
|
||||
'ain.gouv.fr',
|
||||
'ardennes.gouv.fr',
|
||||
'drome.gouv.fr',
|
||||
'bretagne.gouv.fr',
|
||||
'paca.gouv.fr',
|
||||
'haute-saone.gouv.fr',
|
||||
'lot.gouv.fr',
|
||||
'dgfip.finances.gouv.fr',
|
||||
'aveyron.gouv.fr',
|
||||
'gers.gouv.fr',
|
||||
'tarn.gouv.fr',
|
||||
'aude.gouv.fr',
|
||||
'lozere.gouv.fr',
|
||||
'hautes-pyrenees.gouv.fr',
|
||||
'jeunesse-sports.gouv.fr',
|
||||
'alpes.maritimes.gouv.fr',
|
||||
'dreets.gouv.fr',
|
||||
'justice.gouv.fr',
|
||||
'sports.gouv.fr',
|
||||
'nouvelle-aquitaine.gouv.fr',
|
||||
'jura.gouv.fr',
|
||||
'haute-savoie.gouv.fr',
|
||||
'creuse.gouv.fr',
|
||||
'creps-poitiers.sports.gouv.fr',
|
||||
'equipement-agriculture.gouv.fr',
|
||||
'ira-metz.gouv.fr',
|
||||
'loire.gouv.fr',
|
||||
'defense.gouv.fr',
|
||||
'paris.gouv.fr',
|
||||
'ensm.sports.gouv.fr',
|
||||
'isere.gouv.fr',
|
||||
'haute-loire.gouv.fr',
|
||||
'cantal.gouv.fr',
|
||||
'lot-et-garonne.gouv.fr',
|
||||
'reunion.pref.gouv.fr',
|
||||
'loiret.gouv.fr',
|
||||
'indre-et-loire.gouv.fr',
|
||||
'eleve.ira-metz.gouv.fr',
|
||||
'deux-sevres.gouv.fr',
|
||||
'inao.gouv.fr',
|
||||
'franceconnect.gouv.fr',
|
||||
'essone.gouv.fr',
|
||||
'workinfrance.beta.gouv.fr',
|
||||
'seine-saint-denis.gouv.fr',
|
||||
'val-de-marne.gouv.fr',
|
||||
'morbihan.pref.gouv.fr',
|
||||
'externes.justice.gouv.fr',
|
||||
'haute-vienne.gouv.fr',
|
||||
'territoire-de-belfort.gouv.fr',
|
||||
'creps-reunion.sports.gouv.fr',
|
||||
'creps-centre.sports.gouv.fr',
|
||||
'creps-rhonealpes.sports.gouv.fr',
|
||||
'creps-montpellier.sports.gouv.fr',
|
||||
'nord.pref.gouv.fr',
|
||||
'charente-maritime.pref.gouv.fr',
|
||||
'cher.gouv.fr',
|
||||
'cote-dor.gouv.fr',
|
||||
'ssi.gouv.fr',
|
||||
'ira.gouv.fr',
|
||||
'pays-de-la-loire.gouv.fr',
|
||||
'loir-et-cher.gouv.fr',
|
||||
'saone-et-loire.gouv.fr',
|
||||
'enseignementsup.gouv.fr',
|
||||
'eure-et-loir.gouv.fr',
|
||||
'yonne.gouv.fr',
|
||||
'guadeloupe.pref.gouv.fr',
|
||||
'centre-val-de-loire.gouv.fr',
|
||||
'entreprise.api.gouv.fr',
|
||||
'grand-est.gouv.fr',
|
||||
'sarthe.gouv.fr',
|
||||
'sarthe.pref.gouv.fr',
|
||||
'puy-de-dome.gouv.fr',
|
||||
'externes.sante.gouv.fr',
|
||||
'allier.gouv.fr',
|
||||
'aube.gouv.fr',
|
||||
'nievre.gouv.fr',
|
||||
'ardeche.gouv.fr',
|
||||
'api.gouv.fr',
|
||||
'hauts-de-seine.gouv.fr',
|
||||
'hauts-de-france.gouv.fr',
|
||||
'temp-beta.gouv.fr',
|
||||
'def.gouv.fr',
|
||||
'particulier.api.gouv.fr',
|
||||
'ira-lille.gouv.fr',
|
||||
'haute-saone.pref.gouv.fr',
|
||||
'yvelines.pref.gouv.fr',
|
||||
'sgg.pm.gouv.fr',
|
||||
'anah.gouv.fr',
|
||||
'corse.gouv.fr',
|
||||
'mayenne.pref.gouv.fr',
|
||||
'cote-dor.pref.gouv.fr',
|
||||
'guyane.pref.gouv.fr',
|
||||
'ira-nantes.gouv.fr',
|
||||
'igas.gouv.fr',
|
||||
'tarn.pref.gouv.fr',
|
||||
'martinique.gouv.fr',
|
||||
'creps-paca.sports.gouv.fr',
|
||||
'ofb.gouv.fr',
|
||||
'loir-et-cher.pref.gouv.fr',
|
||||
'indre-et-loire.pref.gouv.fr',
|
||||
'polynesie-francaise.pref.gouv.fr',
|
||||
'scl.finances.gouv.fr',
|
||||
'numerique.gouv.fr',
|
||||
'cantal.pref.gouv.fr',
|
||||
'territoire-de-belfort.pref.gouv.fr',
|
||||
'creps-wattignies.sports.gouv.fr',
|
||||
'vienne.pref.gouv.fr',
|
||||
'ardennes.pref.gouv.fr',
|
||||
'creps-strasbourg.sports.gouv.fr',
|
||||
'creps-dijon.sports.gouv.fr',
|
||||
'ara.gouv.fr',
|
||||
'sgdsn.gouv.fr',
|
||||
'pays-de-la-loire.pref.gouv.fr',
|
||||
'anct.gouv.fr',
|
||||
'creps-pap.sports.gouv.fr',
|
||||
'sgae.gouv.fr',
|
||||
'esnm.sports.gouv.fr',
|
||||
'nouvelle-caledonie.gouv.fr',
|
||||
'deets.gouv.fr',
|
||||
'mayotte.gouv.fr',
|
||||
'creps-bordeaux.sports.gouv.fr',
|
||||
'civs.gouv.fr',
|
||||
'iga.interieur.gouv.fr',
|
||||
'cab.travail.gouv.fr',
|
||||
'ira-bastia.gouv.fr',
|
||||
'ira-lyon.gouv.fr',
|
||||
'creps-lorraine.sports.gouv.fr',
|
||||
'dihal.gouv.fr',
|
||||
'ofpra.gouv.fr',
|
||||
'mayotte.pref.gouv.fr',
|
||||
'strategie.gouv.fr',
|
||||
'territoires.gouv.fr',
|
||||
'dgcl.gouv.fr',
|
||||
'doubs.pref.gouv.fr',
|
||||
'service-civique.gouv.fr',
|
||||
'maine-et-loire.pref.gouv.fr',
|
||||
'envsn.sports.gouv.fr',
|
||||
'wallis-et-futuna.pref.gouv.fr',
|
||||
'gendarmerie.defense.gouv.fr',
|
||||
'anlci.gouv.fr',
|
||||
'cabinets.finances.gouv.fr',
|
||||
'seine-maritime.pref.gouv.fr',
|
||||
'promo46.ira-metz.gouv.fr',
|
||||
'aisne.pref.gouv.fr',
|
||||
'sportsdenature.gouv.fr',
|
||||
'loire-atlantique.pref.gouv.fr',
|
||||
'aude.pref.gouv.fr',
|
||||
'premier-ministre.gouv.fr',
|
||||
'igf.finances.gouv.fr',
|
||||
'eleves.ira-bastia.gouv.fr',
|
||||
'igesr.gouv.fr',
|
||||
'alpc.gouv.fr',
|
||||
'externes.emploi.gouv.fr',
|
||||
'prestataire.finances.gouv.fr',
|
||||
'gironde.pref.gouv.fr',
|
||||
'premar-atlantique.gouv.fr',
|
||||
'creps-toulouse.sports.gouv.fr',
|
||||
'guadeloupe.gouv.fr',
|
||||
'cybermalveillance.gouv.fr',
|
||||
'dicod.defense.gouv.fr',
|
||||
'creps-vichy.sports.gouv.fr',
|
||||
'aft.gouv.fr',
|
||||
'equipement.gouv.fr',
|
||||
'academie.defense.gouv.fr',
|
||||
'aube.pref.gouv.fr',
|
||||
'seine-et-marne.pref.gouv.fr',
|
||||
'pyrenees-orientales.pref.gouv.fr',
|
||||
'haute-garonne.pref.gouv.fr',
|
||||
'haut-rhin.pref.gouv.fr',
|
||||
'seine-saint-denis.pref.gouv.fr',
|
||||
'dcstep.gouv.fr',
|
||||
'promo47.ira-metz.gouv.fr',
|
||||
'trackdechets.beta.gouv.fr',
|
||||
'val-de-marne.pref.gouv.fr',
|
||||
'fabrique.social.gouv.fr',
|
||||
'agrasc.gouv.fr',
|
||||
'indre.pref.gouv.fr',
|
||||
'tarn-et-garonne.pref.gouv.fr',
|
||||
'corse.pref.gouv.fr',
|
||||
'bas-rhin.pref.gouv.fr',
|
||||
'inclusion.beta.gouv.fr',
|
||||
'hauts-de-seine.pref.gouv.fr',
|
||||
'loiret.pref.gouv.fr',
|
||||
'essonne.pref.gouv.fr',
|
||||
'territoires-industrie.gouv.fr',
|
||||
'spm975.gouv.fr',
|
||||
'saint-barth-saint-martin.gouv.fr',
|
||||
'judiciaire.interieur.gouv.fr',
|
||||
'mer.gouv.fr',
|
||||
'premar-manche.gouv.fr',
|
||||
'haute-normandie.pref.gouv.fr',
|
||||
'prestataire.modernisation.gouv.fr',
|
||||
'covoiturage.beta.gouv.fr',
|
||||
'promo48.ira-metz.gouv.fr',
|
||||
'france-services.gouv.fr',
|
||||
'ddets.gouv.fr',
|
||||
'afa.gouv.fr',
|
||||
'externes.social.gouv.fr',
|
||||
'vosges.pref.gouv.fr',
|
||||
'reunion.gouv.fr',
|
||||
'rhone.pref.gouv.fr',
|
||||
'alpes-maritimes.pref.gouv.fr',
|
||||
'gard.pref.gouv.fr',
|
||||
'oise.pref.gouv.fr',
|
||||
'creps-reims.sports.gouv.fr',
|
||||
'bouches-du-rhone.pref.gouv.fr',
|
||||
'esante.gouv.fr',
|
||||
'rhone-alpes.pref.gouv.fr',
|
||||
'finistere.pref.gouv.fr',
|
||||
'ops-bss.defense.gouv.fr',
|
||||
'orne.pref.gouv.fr',
|
||||
'transformation.gouv.fr',
|
||||
'cbcm.social.gouv.fr',
|
||||
'recosante.beta.gouv.fr',
|
||||
'pas-de-calais.pref.gouv.fr',
|
||||
'promo49.ira-metz.gouv.fr',
|
||||
'paca.pref.gouv.fr',
|
||||
'meurthe-et-moselle.pref.gouv.fr',
|
||||
'externes.sg.social.gouv.fr',
|
||||
'puy-de-dome.pref.gouv.fr',
|
||||
'academie.def.gouv.fr',
|
||||
'tarn.gouv.frd81intranet.ddcspp.tarn.gouv.fr',
|
||||
'agriculture-equipement.gouv.fr',
|
||||
'creps-idf.sports.gouv.fr',
|
||||
'eleve.ira-nantes.gouv.fr',
|
||||
'cohesion-territoires.gouv.fr',
|
||||
'ariege.pref.gouv.fr',
|
||||
'pyrenees-atlantiques.pref.gouv.fr',
|
||||
'hautes-pyrenees.pref.gouv.fr',
|
||||
'lot-et-garonne.pref.gouv.fr',
|
||||
'loire.pref.gouv.fr',
|
||||
'info-routiere.gouv.fr',
|
||||
'diges.gouv.fr',
|
||||
'insp.gouv.fr',
|
||||
'creps-pdl.sports.gouv.fr',
|
||||
'ddc.social.gouv.fr',
|
||||
'eleve.insp.gouv.fr',
|
||||
'val-doise.pref.gouv.fr',
|
||||
'montsaintmichel.gouv.fr',
|
||||
'st-cyr.terre-net.defense.gouv.fr',
|
||||
'.finances.gouv.fr',
|
||||
'logement.gouv.fr',
|
||||
'cotes-darmor.pref.gouv.fr',
|
||||
'marne.pref.gouv.fr',
|
||||
'herault.pref.gouv.fr',
|
||||
'viennne.gouv.fr',
|
||||
'landes.pref.gouv.fr',
|
||||
'moselle.pref.gouv.fr',
|
||||
'saone-et-loire.pref.gouv.fr',
|
||||
'bmpm.gouv.fr',
|
||||
'ecologie-territoires.gouv.fr',
|
||||
'nievre.pref.gouv.fr',
|
||||
'hautes-pyrénées.gouv.fr',
|
||||
'gic.gouv.fr',
|
||||
'industrie.gouv.fr',
|
||||
'lot.pref.gouv.fr',
|
||||
'plan.gouv.fr',
|
||||
'internet.gouv.fr',
|
||||
'mesads.beta.gouv.fr',
|
||||
'gers.pref.gouv.fr',
|
||||
'dordogne.pref.gouv.fr',
|
||||
'somme.pref.gouv.fr',
|
||||
'datasubvention.beta.gouv.fr',
|
||||
'anc.gouv.fr',
|
||||
'premar-mediterranee.gouv.fr',
|
||||
'ille-et-vilaine.pref.gouv.fr',
|
||||
'eure-et-loir.pref.gouv.fr',
|
||||
'prestataires.pm.gouv.fr',
|
||||
'snu.gouv.fr',
|
||||
'code.gouv.fr',
|
||||
'alsace.pref.gouv.fr',
|
||||
'haute-vienne.pref.gouv.fr',
|
||||
'yonne.pref.gouv.fr',
|
||||
'bretagne.pref.gouv.fr',
|
||||
'mastere.insp.gouv.fr',
|
||||
'cada.pm.gouv.fr',
|
||||
'creuse.pref.gouv.fr',
|
||||
'ecologie.gouv.fr',
|
||||
'midi-pyrenees.pref.gouv.fr',
|
||||
'promo54.ira-metz.gouv.fr',
|
||||
'var.pref.gouv.fr',
|
||||
'alpes-de-haute-provence.pref.gouv.fr',
|
||||
'mail.numerique.gouv.fr',
|
||||
'france-identite.gouv.fr',
|
||||
'transport.data.gouv.fr',
|
||||
'allier.pref.gouv.fr',
|
||||
'dilhal.gouv.fr',
|
||||
'ardeche.pref.gouv.fr',
|
||||
'haute-corse.pref.gouv.fr',
|
||||
'intérieur.gouv.fr',
|
||||
'ddfip.gouv.fr',
|
||||
'calvados.pref.gouv.fr',
|
||||
'territoir-de-belfort.gouv.fr',
|
||||
'nor.gouv.fr',
|
||||
'creps-occitanie.sports.gouv.fr',
|
||||
'developpement-durabe.gouv.fr',
|
||||
'educ.nat.gouv.fr',
|
||||
'developpement-duable.gouv.fr',
|
||||
'dgfip.finanes.gouv.fr',
|
||||
'loire-atlantqieu.gouv.fr',
|
||||
'promo55.ira-metz.gouv.fr',
|
||||
'haute-saône.gouv.fr',
|
||||
'developpement.durable.gouv.fr',
|
||||
'dreet.gouv.fr',
|
||||
'miprof.gouv.fr',
|
||||
'pref.guyane.gouv.fr',
|
||||
'developpement.gouv.fr',
|
||||
'gendamrerie.interieur.gouv.fr',
|
||||
'pyrenees-atlantique.gouv.fr',
|
||||
'apprentissage.beta.gouv.fr',
|
||||
'yveliens.gouv.fr',
|
||||
'justiice.gouv.fr',
|
||||
'cutlure.gouv.fr',
|
||||
'aidantsconnect.beta.gouv.fr',
|
||||
'developpement-durbale.gouv.fr',
|
||||
'sine-et-marne.gouv.fr',
|
||||
'sociale.gouv.fr',
|
||||
'develeoppement-durable.gouv.fr',
|
||||
'draaf.gouv.fr',
|
||||
'drets.gouv.fr',
|
||||
'ancli.gouv.fr',
|
||||
'finistrere.gouv.fr',
|
||||
'bourgogne.pref.gouv.fr',
|
||||
'ac-polynesie.pf',
|
||||
'ac-lille.fr',
|
||||
'ac-nantes.fr',
|
||||
'ac-martinique.fr',
|
||||
'ac-creteil.fr',
|
||||
'ac-toulouse.fr',
|
||||
'ac-amiensfr',
|
||||
'ac-amiens.fr',
|
||||
'ac-rennes.fr',
|
||||
'ac-strasbourg.fr',
|
||||
'ac-lyon.fr',
|
||||
'ac-versailles.fr',
|
||||
'ac-audit.fr',
|
||||
'ac-rouen.fr',
|
||||
'ac-reunion.fr',
|
||||
'ac-poitiers.fr',
|
||||
'ac-caen.fr',
|
||||
'ac-montpellier.fr',
|
||||
'ac-paris.fr',
|
||||
'ac-besancon.fr',
|
||||
'ac-nancy-metz.fr',
|
||||
'ac-aix-marseille.fr',
|
||||
'ac-grenoble.fr',
|
||||
'ac-corse.fr',
|
||||
'ac-nice.fr',
|
||||
'ac-orleans-tours.fr',
|
||||
'ac-guadeloupe.fr',
|
||||
'ac-reims.fr',
|
||||
'ac-mayotte.fr',
|
||||
'ac-clermont.fr',
|
||||
'ac-bordeaux.fr',
|
||||
'ac-limoges.fr',
|
||||
'ac-normandie.fr',
|
||||
'ac-dijon.fr',
|
||||
'ac-guyane.fr',
|
||||
'ac-transports.fr',
|
||||
'ac-arpajonnais.com',
|
||||
'ac-cned.fr',
|
||||
'ac-nettoyage.com',
|
||||
'ac-architectes.fr',
|
||||
'ac-ajaccio.corsica',
|
||||
'ac-noumea.nc',
|
||||
'ac-spm.fr',
|
||||
'ac-versailes.fr',
|
||||
'ac-polynesie.fr',
|
||||
'ac-experts.fr',
|
||||
'ac-creteil.com',
|
||||
'ac-smart-relocation.com',
|
||||
'ac-ec.pro',
|
||||
'ac-sas.fr',
|
||||
'ac-derma.de',
|
||||
'ac-or.com',
|
||||
'ac-baugeois.fr',
|
||||
'ac-5.ru',
|
||||
'ac-arles.fr',
|
||||
'ac-holding.net',
|
||||
'ac-mb.fr',
|
||||
'ac-wf.wf',
|
||||
'ac-brest-finistere.fr',
|
||||
'ac-leman.com',
|
||||
'ac-darboussier.fr',
|
||||
'ac-si.fr',
|
||||
'ac-bordeau.fr',
|
||||
'ac-gatinais.com',
|
||||
'ac-cheminots.fr',
|
||||
'ac-seyssinet.com',
|
||||
'ac-cannes.fr',
|
||||
'ac-prev.com',
|
||||
'ac-sologne.fr',
|
||||
'ac-rennes',
|
||||
'ac-courbevoie.com',
|
||||
'ac-ce.fr',
|
||||
'ac-architecte.fr',
|
||||
'ac-tions.org',
|
||||
'ac-pm.fr',
|
||||
'ac-avocats.com',
|
||||
'ac-talents-rh.com',
|
||||
'ac-louis.com',
|
||||
'ac-internet.fr',
|
||||
'ac-toulouse.com',
|
||||
'ac-escial.fr',
|
||||
'ac-environnement.com',
|
||||
'ac-academie.fr',
|
||||
'ac-poiters.fr',
|
||||
'ac-bordeux.fr',
|
||||
'ac-verseilles.fr',
|
||||
'ac-ais-marseille.fr',
|
||||
'ac-horizon.fr',
|
||||
'ac-bordeaux.ft',
|
||||
'ac-toulouses.fr',
|
||||
'ac-toulous.fr'
|
||||
].freeze
|
||||
|
||||
def check(email:)
|
||||
return { success: false } if email.blank?
|
||||
|
||||
parsed_email = Mail::Address.new(EmailSanitizableConcern::EmailSanitizer.sanitize(email))
|
||||
return { success: false } if parsed_email.domain.blank?
|
||||
|
||||
return { success: true } if KNOWN_DOMAINS.any? { _1 == parsed_email.domain }
|
||||
|
||||
similar_domains = closest_domains(domain: parsed_email.domain)
|
||||
return { success: true } if similar_domains.empty?
|
||||
|
||||
{ success: true, email_suggestions: email_suggestions(parsed_email:, similar_domains:) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def closest_domains(domain:)
|
||||
KNOWN_DOMAINS.filter do |known_domain|
|
||||
close_by_distance_of(domain, known_domain, distance: 1) ||
|
||||
with_same_chars_and_close_by_distance_of(domain, known_domain, distance: 2)
|
||||
end
|
||||
end
|
||||
|
||||
def close_by_distance_of(a, b, distance:)
|
||||
String::Similarity.levenshtein_distance(a, b) == distance
|
||||
end
|
||||
|
||||
def with_same_chars_and_close_by_distance_of(a, b, distance:)
|
||||
close_by_distance_of(a, b, distance: 2) && a.chars.sort == b.chars.sort
|
||||
end
|
||||
|
||||
def email_suggestions(parsed_email:, similar_domains:)
|
||||
similar_domains.map { Mail::Address.new("#{parsed_email.local}@#{_1}").to_s }
|
||||
end
|
||||
end
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -161,6 +161,7 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
get 'password_complexity' => 'password_complexity#show', as: 'show_password_complexity'
|
||||
get 'check_email' => 'email_checker#show', as: 'show_email_suggestions'
|
||||
|
||||
resources :targeted_user_links, only: [:show]
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@
|
|||
"core-js": "^3.37.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"debounce": "^1.2.1",
|
||||
"email-butler": "^1.0.13",
|
||||
"geojson": "^0.5.0",
|
||||
"graphiql": "^3.2.3",
|
||||
"graphql": "^16.8.1",
|
||||
|
|
39
spec/controllers/email_checker_controller_spec.rb
Normal file
39
spec/controllers/email_checker_controller_spec.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
describe EmailCheckerController, type: :controller do
|
||||
describe '#show' do
|
||||
render_views
|
||||
before { get :show, format: :json, params: params }
|
||||
let(:body) { JSON.parse(response.body, symbolize_names: true) }
|
||||
|
||||
context 'valid email' do
|
||||
let(:params) { { email: 'martin@orange.fr' } }
|
||||
it do
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(body).to eq({ success: true })
|
||||
end
|
||||
end
|
||||
|
||||
context 'email with typo' do
|
||||
let(:params) { { email: 'martin@orane.fr' } }
|
||||
it do
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(body).to eq({ success: true, email_suggestions: ['martin@orange.fr'] })
|
||||
end
|
||||
end
|
||||
|
||||
context 'empty' do
|
||||
let(:params) { { email: '' } }
|
||||
it do
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(body).to eq({ success: false })
|
||||
end
|
||||
end
|
||||
|
||||
context 'notanemail' do
|
||||
let(:params) { { email: 'clarkkent' } }
|
||||
it do
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(body).to eq({ success: false })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
spec/lib/email_checker_spec.rb
Normal file
36
spec/lib/email_checker_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
describe EmailChecker do
|
||||
describe 'check' do
|
||||
subject { described_class.new }
|
||||
|
||||
it 'works with identified use cases' do
|
||||
expect(subject.check(email: nil)).to eq({ success: false })
|
||||
expect(subject.check(email: '')).to eq({ success: false })
|
||||
expect(subject.check(email: 'panpan')).to eq({ success: false })
|
||||
|
||||
# allow same domain
|
||||
expect(subject.check(email: "martin@orange.fr")).to eq({ success: true })
|
||||
# find difference of 1 lev distance
|
||||
expect(subject.check(email: "martin@orane.fr")).to eq({ success: true, email_suggestions: ['martin@orange.fr'] })
|
||||
# find difference of 2 lev distance, only with same chars
|
||||
expect(subject.check(email: "martin@oragne.fr")).to eq({ success: true, email_suggestions: ['martin@orange.fr'] })
|
||||
# ignore unknown domain
|
||||
expect(subject.check(email: "martin@ore.fr")).to eq({ success: true })
|
||||
end
|
||||
|
||||
it 'passes through real use cases, with levenshtein_distance 1' do
|
||||
expect(subject.check(email: "martin@asn.com")).to eq({ success: true, email_suggestions: ['martin@msn.com'] })
|
||||
expect(subject.check(email: "martin@gamail.com")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] })
|
||||
expect(subject.check(email: "martin@glail.com")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] })
|
||||
expect(subject.check(email: "martin@gmail.coml")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] })
|
||||
expect(subject.check(email: "martin@gmail.con")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] })
|
||||
expect(subject.check(email: "martin@hotmil.fr")).to eq({ success: true, email_suggestions: ['martin@hotmail.fr'] })
|
||||
expect(subject.check(email: "martin@mail.com")).to eq({ success: true, email_suggestions: ["martin@gmail.com", "martin@ymail.com", "martin@mailo.com"] })
|
||||
expect(subject.check(email: "martin@msc.com")).to eq({ success: true, email_suggestions: ["martin@msn.com", "martin@mac.com"] })
|
||||
expect(subject.check(email: "martin@ymail.com")).to eq({ success: true })
|
||||
end
|
||||
|
||||
it 'passes through real use cases, with levenshtein_distance 2, must share all chars' do
|
||||
expect(subject.check(email: "martin@oise.fr")).to eq({ success: true }) # could be live.fr
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue