Merge pull request #1 from covidpass-org/main

Merge with original repo
This commit is contained in:
JantsoP 2021-07-25 11:19:37 +03:00 committed by GitHub
commit 4a4120aebb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 254 additions and 65 deletions

View File

@ -10,7 +10,7 @@ function Alert(props: AlertProps): JSX.Element {
return (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 mt-5 rounded relative" role="alert">
<span className="block sm:inline" id="message">{props.errorMessage}</span>
<span className="block sm:inline pr-6" id="message">{props.errorMessage}</span>
<span className="absolute top-0 bottom-0 right-0 px-4 py-3" onClick={props.onClose}>
<svg className="fill-current h-6 w-6 text-red-500" role="button" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">

View File

@ -29,6 +29,11 @@ function Form(): JSX.Element {
// Check if there is a translation and replace message accordingly
const setErrorMessage = (message: string) => {
if (message == undefined) {
_setErrorMessage(undefined);
return;
}
const translation = t('errors:'.concat(message));
_setErrorMessage(translation !== message ? translation : message);
};
@ -68,7 +73,20 @@ function Form(): JSX.Element {
const codeReader = new BrowserQRCodeReader();
// Needs to be called before any camera can be accessed
await BrowserQRCodeReader.listVideoInputDevices();
let deviceList: MediaDeviceInfo[];
try {
deviceList = await BrowserQRCodeReader.listVideoInputDevices();
} catch (e) {
setErrorMessage('noCameraAccess');
return;
}
// Check if camera device is present
if (deviceList.length == 0) {
setErrorMessage("noCameraFound");
return;
}
// Get preview Element to show camera stream
const previewElem: HTMLVideoElement = document.querySelector('#cameraPreview');
@ -89,6 +107,9 @@ function Form(): JSX.Element {
setGlobalControls(undefined);
setIsCameraOpen(false);
}
if (error !== undefined) {
setErrorMessage(error.message);
}
}
)
);
@ -101,6 +122,12 @@ function Form(): JSX.Element {
event.preventDefault();
setLoading(true);
if(navigator.userAgent.match('CriOS')) {
setErrorMessage('safariSupportOnly');
setLoading(false);
return;
}
if (!file && !qrCode) {
setErrorMessage('noFileOrQrCode')
setLoading(false);
@ -228,7 +255,7 @@ function Form(): JSX.Element {
{t('index:addToWallet')}
</button>
<div id="spin" className={loading ? undefined : "hidden"}>
<svg className="animate-spin h-5 w-5 ml-2" viewBox="0 0 24 24">
<svg className="animate-spin h-5 w-5 ml-3" viewBox="0 0 24 24">
<circle className="opacity-0" cx="12" cy="12" r="10" stroke="currentColor"
strokeWidth="4"/>
<path className="opacity-75" fill="currentColor"

View File

@ -14,4 +14,7 @@ couldNotFindQrCode: QR-Code konnte in der ausgewählten Datei nicht gefunden wer
invalidQrCode: Ungültiger QR-Code
certificateType: Kein gültiger Zertifikatstyp gefunden
invalidTestResult: Ungültiges Testergebnis
invalidTestType: Ungültiger Testtyp
invalidTestType: Ungültiger Testtyp
noCameraAccess: Kein Zugriff auf die Kamera möglich. Überprüfe die Berechtigungen unter Einstellungen > Safari > Kamera.
noCameraFound: Keine Kamera gefunden.
safariSupportOnly: Bitte verwende unter iOS den Safari Browser.

View File

@ -14,4 +14,7 @@ couldNotFindQrCode: Could not find QR Code in provided file
invalidQrCode: Invalid QR code
certificateType: No valid certificate type found
invalidTestResult: Invalid test result
invalidTestType: Invalid test type
invalidTestType: Invalid test type
noCameraAccess: Could not access camera. Check permissions under Settings > Safari > Camera.
noCameraFound: Could not find camera.
safariSupportOnly: On iOS, please use the Safari Browser.

View File

@ -21,7 +21,7 @@ dataPrivacyFaq: data privacy FAQ
contact: Contact
email: Email
website: Website
process: Simplified of the process
process: Simplified explanation of the process
processFirst: First, the following steps happen locally in your browser
processSecond: Second, the following steps happen on our server
processThird: Finally, the following steps happen locally in your browser

1
public/locales/fi-FI Symbolic link
View File

@ -0,0 +1 @@
fi

View File

@ -2,7 +2,7 @@ noFileOrQrCode: Veuillez scanner un QR Code ou sélectionner un fichier
signatureFailed: Erreur lors de la signature du pass sur le serveur
decodingFailed: Échec du décodage du QR Code
invalidColor: Couleur non valide
vaccinationInfo: Échec de la lecture des informations de vaccination
certificateData: Échec de la lecture des données du certificat
nameMissing: Échec de la lecture du nom
dobMissing: Échec de la lecture de la date de naissance
invalidMedicalProduct: Produit vaccinal non valide
@ -12,3 +12,9 @@ invalidFileType: Type de fichier non valide
couldNotDecode: Impossible de décoder le QR Code du fichier
couldNotFindQrCode: Impossible de trouver le QR Code dans le fichier fourni
invalidQrCode: QR Code non valide
certificateType: Aucun type de certificat valide n'a été trouvé
invalidTestResult: Résultat du test non valide
invalidTestType: Type de test non valide
noCameraAccess: Impossible d'accéder à la caméra. Vérifiez les autorisations sous Paramètres > Safari > Appareil photo.
noCameraFound: Impossible de trouver la caméra.
safariSupportOnly: Sur iOS, veuillez utiliser le navigateur Safari.

View File

@ -7,7 +7,7 @@ selectCertificateDescription: |
stopCamera: Arrêter l'appareil photo
startCamera: Démarrer l'appareil photo
openFile: Sélectionner un fichier
foundQrCode: Code QR trouvé!
foundQrCode: Code QR trouvé !
pickColor: Choisissez une couleur
pickColorDescription: Choisissez une couleur de fond pour votre passe.
colorWhite: blanc

View File

@ -21,7 +21,7 @@ dataPrivacyFaq: FAQ sur la confidentialité des données
contact: Contact
email: Email
website: Site web
process: Simplification du processus
process: Explication simplifiée du processus
processFirst: Premièrement, les étapes suivantes se déroulent localement dans votre navigateur.
processSecond: Ensuite, les étapes suivantes se déroulent sur notre serveur
processThird: Enfin, les étapes suivantes se déroulent localement dans votre navigateur
@ -49,9 +49,9 @@ logFilesTime: La date et l'heure de l'accès.
logFilesIpAddress: Les adresses IP pseudonymisées
rights: Vos droits
rightsGranted: Conformément au RGPD, vous disposez des droits suivants
rightsAccess: Droit d'accès à vos données; Vous avez le droit de savoir quelles données ont été collectées à votre sujet et comment elles ont été traitées.
rightsErasure: Droit à l'oubli; Effacement de vos données personnelles.
rightsRectification: Droit de rectification; Vous avez le droit de corriger des données inexactes.
rightsPortability: Droit à la portabilité des données; Vous avez le droit de transférer vos données d'un système de traitement à un autre.
rightsAccess: Droit d'accès à vos données ; Vous avez le droit de savoir quelles données ont été collectées à votre sujet et comment elles ont été traitées.
rightsErasure: Droit à l'oubli ; Effacement de vos données personnelles.
rightsRectification: Droit de rectification ; Vous avez le droit de corriger des données inexactes.
rightsPortability: Droit à la portabilité des données ; Vous avez le droit de transférer vos données d'un système de traitement à un autre.
thirdParties: Tiers liés
appleSync: Apple peut synchroniser vos passes via iCloud.

1
public/locales/nl-BE Symbolic link
View File

@ -0,0 +1 @@
nl

1
public/locales/nl-NL Symbolic link
View File

@ -0,0 +1 @@
nl

View File

@ -0,0 +1,6 @@
title: CovidPass
subtitle: Voeg uw EU Digitaal COVID-certificaten toe aan uw favoriete wallet-apps.
privacyPolicy: Privacybeleid
donate: Doneer
gitHub: GitHub
imprint: Over

View File

@ -0,0 +1,20 @@
noFileOrQrCode: Scan een QR-code of selecteer een bestand
signatureFailed: Fout bij het ondertekenen van de pass op de server
decodingFailed: Kan de payload van de QR-code niet decoderen
invalidColor: Ongeldige kleur
certificateData: Kan certificaatgegevens niet lezen
nameMissing: Kan naam niet lezen
dobMissing: Kan geboortedatum niet lezen
invalidMedicalProduct: Ongeldig medisch product
invalidCountryCode: Ongeldige landcode
invalidManufacturer: Ongeldige fabrikant
invalidFileType: Ongeldig bestandstype
couldNotDecode: Kon QR-code niet uit bestand decoderen
couldNotFindQrCode: Kon de QR-code niet vinden in het verstrekte bestand
invalidQrCode: Ongeldige QR-code
certificateType: Geen geldig certificaattype gevonden
invalidTestResult: Ongeldig testresultaat
invalidTestType: Ongeldig testtype
noCameraAccess: Kon geen toegang krijgen tot de camera. Controleer de machtigingen onder Instellingen > Safari > Camera.
noCameraFound: Ik kon de camera niet vinden.
safariSupportOnly: Gebruik op iOS de Safari-browser.

View File

@ -0,0 +1,27 @@
heading: Informatie volgens § 5 TMG
contact: Contact
euDisputeResolution: EU-geschillenbeslechting
euDisputeResolutionParagraph: |
De Europese Commissie biedt een platform voor online geschillenbeslechting (OS) https://ec.europa.eu/consumers/odr.
U vindt ons e-mailadres in de conrtactgegevens hierboven.
consumerDisputeResolution: Beslechting van consumentengeschillen / universele arbitragecommissie
consumerDisputeResolutionParagraph: We zijn niet bereid of verplicht om deel te nemen aan geschillenbeslechtingsprocedures voor een arbitragecommissie voor consumenten.
liabilityForContents: Aansprakelijkheid voor inhoud
liabilityForContentsParagraph: |
Als dienstverlener zijn wij verantwoordelijk voor onze eigen inhoud op deze pagina's in overeenstemming met § 7 lid 1 TMG onder de algemene wetten.
Volgens §§ 8 tot 10 TMG zijn wij niet verplicht om verzonden of opgeslagen informatie te controleren of omstandigheden te onderzoeken die wijzen op illegale activiteiten.
Verplichtingen tot het verwijderen of blokkeren van het gebruik van informatie onder de algemene wetten blijven onaangetast.
Aansprakelijkheid ter zake is echter pas mogelijk vanaf het moment dat een concrete wetsovertreding bekend wordt.
Als we kennis krijgen van dergelijke inbreuken, zullen we de relevante inhoud onmiddellijk verwijderen.
liabilityForLinks: Aansprakelijkheid voor links
liabilityForLinksParagraph: |
Ons aanbod bevat links naar externe websites van derden, op wiens inhoud wij geen invloed hebben.
Daarom kunnen wij voor deze externe inhoud geen aansprakelijkheid aanvaarden.
De respectievelijke aanbieder of exploitant van de sites is altijd verantwoordelijk voor de inhoud van de gelinkte sites.
De gelinkte pagina's werden op het moment van linken gecontroleerd op mogelijke juridische overtredingen.
Illegale inhoud was op het moment van linken niet herkenbaar.
Een permanente controle van de inhoud van de gelinkte pagina's is echter niet redelijk zonder concreet bewijs van een overtreding van de wet.
Als we inbreuken vaststellen, zullen we dergelijke links onmiddellijk verwijderen.
credits: Credits
creditsSource: Met fragmenten van https://www.e-recht24.de/impressum-generator.html
creditsTranslation: Vertaald met https://www.DeepL.com/Translator (gratis versie)

View File

@ -0,0 +1,26 @@
iosHint: Gebruik op iOS de Safari-browser.
errorClose: Sluiten
selectCertificate: Selecteer Certificaat
selectCertificateDescription: |
Scan de QR-code op uw certificaat of selecteer een screenshot of pdf-pagina met de QR-code.
Merk op dat het rechtstreeks vanaf de camera selecteren van een bestand niet wordt ondersteund.
stopCamera: Stop Camera
startCamera: Start Camera
openFile: Selecteer Bestand
foundQrCode: QR-code gevonden!
pickColor: Kies een kleur
pickColorDescription: Kies een achtergrondkleur voor je pas.
colorWhite: wit
colorBlack: zwart
colorGrey: grijs
colorGreen: groen
colorIndigo: indigo
colorBlue: blauw
colorPurple: paars
colorTeal: groenblauw
addToWallet: Toevoegen aan Wallet
dataPrivacyDescription: |
Gegevensprivacy is van bijzonder belang bij de verwerking van gezondheidsgerelateerde gegevens.
Om een weloverwogen beslissing te kunnen nemen, lees a.u.b. de
iAcceptThe: Ik accepteer het
privacyPolicy: Privacybeleid

View File

@ -0,0 +1,57 @@
gdprNotice: |
Ons privacybeleid is gebaseerd op de voorwaarden die worden gebruikt door de Europese wetgever
voor de vaststelling van de Algemene Verordening Gegevensbescherming (AVG).
generalInfo: Algemene informatie
generalInfoProcess: |
Het hele proces van het genereren van het pass-bestand gebeurt lokaal in uw browser.
Voor de ondertekeningsstap wordt alleen een gehashte weergave van uw gegevens naar de server gestuurd.
generalInfoStoring: Uw gegevens worden niet opgeslagen buiten de actieve browsersessie en de site maakt geen gebruik van cookies.
generalInfoThirdParties: Er worden geen gegevens naar derden verzonden.
generalInfoHttps: Wij verzenden uw gegevens veilig via https.
generalInfoLocation: Onze server wordt gehost in Neurenberg, Duitsland.
generalInfoGitHub: De broncode van deze site is beschikbaar op
generalInfoLockScreen: Standaard zijn Apple Wallet-passen toegankelijk vanaf het vergrendelingsscherm. Dit kan worden gewijzigd in de
settings: settings
generalInfoProvider: |
De serverprovider verwerkt gegevens om deze site ter beschikking te stellen.
Om beter te begrijpen welke maatregelen zij nemen om uw gegevens te beschermen, leest u ook hun
privacyPolicy: privacybeleid
andThe: en de
dataPrivacyFaq: veelgestelde vragen over gegevensprivacy
contact: Contact
email: E-mail
website: Website
process: Vereenvoudigde uitleg van het proces
processFirst: Ten eerste gebeuren de volgende stappen lokaal in uw browser
processSecond: Ten tweede gebeuren de volgende stappen op onze server
processThird: Ten slotte gebeuren de volgende stappen lokaal in uw browser
processRecognizing: Herkennen en extraheren van de QR-codegegevens van uw geselecteerde certificaat
processDecoding: Het decoderen van uw persoonlijke en gezondheidsgerelateerde gegevens uit de QR-code payload
processAssembling: Een onvolledig pasbestand samenstellen uit uw gegevens
processGenerating: Een bestand genereren dat hashes bevat van de gegevens die zijn opgeslagen in het pasbestand
processSending: Alleen het bestand met de hashes naar onze server verzenden
processReceiving: De hashes ontvangen en controleren die lokaal zijn gegenereerd
processSigning: Het bestand met de hashes ondertekenen
processSendingBack: De handtekening terugsturen
processCompleting: Het getekende pass-bestand samenstellen uit het onvolledige bestand dat lokaal is gegenereerd en de handtekening
processSaving: Het bestand op uw apparaat opslaan
locallyProcessedData: Lokaal verwerkte gegevens
the: Het
schema: Digitaal Covid Certificate Schema
specification: bevat een gedetailleerde specificatie van welke gegevens in de QR-code kunnen staan en in uw browser worden verwerkt.
serverProvider: Serverprovider
serverProviderIs: Onze serverprovider is
logFiles: De volgende gegevens kunnen worden verzameld en opgeslagen in de serverlogbestanden
logFilesBrowser: De gebruikte browsertypes en -versies
logFilesOs: Het besturingssysteem dat wordt gebruikt door het toegangssysteem
logFilesReferrer: De website van waaruit een toegangssysteem onze website bereikt (zogenaamde verwijzers)
logFilesTime: De datum en tijd van bezoek
logFilesIpAddress: De gepseudonimiseerde IP-adressen
rights: Jou rechten
rightsGranted: Conform de AVG heeft u de volgende rechten
rightsAccess: Recht op toegang tot uw gegevens; U hebt het recht om te weten welke gegevens over u zijn verzameld en hoe deze zijn verwerkt.
rightsErasure: Recht om vergeten te worden; Wissen van uw persoonsgegevens.
rightsRectification: Recht op rectificatie; U heeft het recht om onjuiste gegevens te corrigeren.
rightsPortability: Recht op gegevensoverdraagbaarheid; U heeft het recht om uw gegevens over te dragen van het ene verwerkingssysteem naar het andere.
thirdParties: Derden gekoppeld
appleSync: Apple kan uw passen synchroniseren via iCloud

View File

@ -2,7 +2,7 @@ import {ValueSets} from "./value_sets";
import {Constants} from "./constants";
enum CertificateType {
Vaccination = 'Vaccination',
Vaccine = 'Vaccine',
Test = 'Test',
Recovery = 'Recovery',
}
@ -63,25 +63,35 @@ export class Payload {
throw new Error('certificateData');
}
// Get name and date of birth information
// Get name information
const nameInformation = covidCertificate['nam'];
const dateOfBirthInformation = covidCertificate['dob'];
if (nameInformation == undefined) {
throw new Error('nameMissing');
}
if (dateOfBirthInformation == undefined) {
throw new Error('dobMissing');
}
const name = `${nameInformation['fn']}, ${nameInformation['gn']}`;
const dateOfBirth = dateOfBirthInformation;
const firstName = nameInformation['gn'];
const lastName = nameInformation['fn'];
const transliteratedFirstName = nameInformation['gnt'];
const transliteratedLastName = nameInformation['fnt'];
// Check if name contains non-latin characters
const nameRegex = new RegExp('^(\\p{Script=Latin}|[ -\'`´])+$', 'u');
let name: string;
if (nameRegex.test(firstName) && nameRegex.test(lastName)) {
name = `${firstName} ${lastName}`;
} else {
name = `${transliteratedFirstName} ${transliteratedLastName}`;
}
let properties: object;
// Set certificate type and properties
if (covidCertificate['v'] !== undefined) {
this.certificateType = CertificateType.Vaccination;
this.certificateType = CertificateType.Vaccine;
properties = covidCertificate['v'][0];
}
if (covidCertificate['t'] !== undefined) {
@ -96,23 +106,16 @@ export class Payload {
throw new Error('certificateType')
}
// Get country code, identifier and issuer
const countryCode = properties['co'];
// Get identifier and issuer
const uvci = properties['ci'];
const certificateIssuer = properties['is'];
if (!(countryCode in valueSets.countryCodes)) {
throw new Error('invalidCountryCode');
}
const country = valueSets.countryCodes[countryCode].display;
const generic: PassDictionary = {
headerFields: [
{
key: "type",
label: "Certificate Type",
value: this.certificateType
label: "EU Digital COVID",
value: this.certificateType + " Certificate"
}
],
primaryFields: [
@ -123,14 +126,7 @@ export class Payload {
}
],
secondaryFields: [],
auxiliaryFields: [
{
key: "dob",
label: "Date of Birth",
value: dateOfBirth,
textAlignment: TextAlignment.right
}
],
auxiliaryFields: [],
backFields: [
{
key: "uvci",
@ -141,11 +137,6 @@ export class Payload {
key: "issuer",
label: "Certificate Issuer",
value: certificateIssuer
},
{
key: "country",
label: "Country",
value: country
}
]
}
@ -164,8 +155,17 @@ export class Payload {
}
static fillPassData(type: CertificateType, data: PassDictionary, properties: Object, valueSets: ValueSets): PassDictionary {
// Get country name
const countryCode = properties['co'];
if (!(countryCode in valueSets.countryCodes)) {
throw new Error('invalidCountryCode');
}
const country = valueSets.countryCodes[countryCode].display;
switch (type) {
case CertificateType.Vaccination:
case CertificateType.Vaccine:
const dose = `${properties['dn']}/${properties['sd']}`;
const dateOfVaccination = properties['dt'];
const medialProductKey = properties['mp'];
@ -178,7 +178,7 @@ export class Payload {
throw new Error('invalidManufacturer')
}
const vaccineName = valueSets.medicalProducts[medialProductKey].display;
const vaccineName = valueSets.medicalProducts[medialProductKey].display.replace(/\s*\([^)]*\)\s*/g, "");
const manufacturer = valueSets.manufacturers[manufacturerKey].display;
data.secondaryFields.push(...[
@ -194,11 +194,19 @@ export class Payload {
textAlignment: TextAlignment.right
}
]);
data.auxiliaryFields.splice(0, 0, {
key: "vaccine",
label: "Vaccine",
value: vaccineName
});
data.auxiliaryFields.push(...[
{
key: "vaccine",
label: "Vaccine",
value: vaccineName
},
{
key: "cov",
label: "Country of Vaccination",
value: country,
textAlignment: TextAlignment.right
}
]);
data.backFields.push(...[
{
key: "manufacturer",
@ -234,7 +242,7 @@ export class Payload {
data.secondaryFields.push(...[
{
key: "result",
label: "Result",
label: "Test Result",
value: testResult
},
{
@ -244,7 +252,6 @@ export class Payload {
textAlignment: TextAlignment.right
}
]);
data.auxiliaryFields.pop();
data.auxiliaryFields.push(...[
{
key: "test",
@ -258,6 +265,11 @@ export class Payload {
textAlignment: TextAlignment.right
},
]);
data.backFields.push({
key: "cot",
label: "Country of Test",
value: country
});
if (testingCentre !== undefined)
data.backFields.push({
key: "centre",
@ -276,29 +288,28 @@ export class Payload {
const validUntil = properties['du'];
data.secondaryFields.push(...[
{
key: "result",
label: "Test Result",
value: "Detected"
},
{
key: "from",
label: "Valid From",
value: validFrom,
},
{
key: "dov",
label: "Date of positive Test",
value: firstPositiveTestDate,
textAlignment: TextAlignment.right
}
]);
data.auxiliaryFields.pop();
data.auxiliaryFields.push(...[
{
key: "testdate",
label: "Test Date",
value: firstPositiveTestDate
},
{
key: "until",
label: "Valid Until",
value: validUntil,
},
{
key: "cov",
label: "Country of Test",
value: country,
textAlignment: TextAlignment.right
},
]);