mirror of
https://github.com/covidpass-org/covidpass.git
synced 2025-02-16 11:41:19 +01:00
Merge pull request #27 from covidpass-org/localization
Localization and German translation
This commit is contained in:
commit
12dd289752
@ -11,7 +11,7 @@ FROM node:14-alpine AS builder
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
RUN npm run build
|
RUN yarn build
|
||||||
|
|
||||||
# Production image, copy all the files and run next
|
# Production image, copy all the files and run next
|
||||||
FROM node:14-alpine AS runner
|
FROM node:14-alpine AS runner
|
||||||
@ -23,7 +23,8 @@ RUN addgroup -g 1001 -S nodejs
|
|||||||
RUN adduser -S nextjs -u 1001
|
RUN adduser -S nextjs -u 1001
|
||||||
|
|
||||||
# You only need to copy next.config.js if you are NOT using the default configuration
|
# You only need to copy next.config.js if you are NOT using the default configuration
|
||||||
# COPY --from=builder /app/next.config.js ./
|
COPY --from=builder /app/next.config.js ./next.config.js
|
||||||
|
COPY --from=builder /app/next-i18next.config.js ./next-i18next.config.js
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
|
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
|
||||||
interface AlertProps {
|
interface AlertProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Alert(props: AlertProps): JSX.Element {
|
function Alert(props: AlertProps): JSX.Element {
|
||||||
|
const { t } = useTranslation(['index', 'errors']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 mt-5 rounded relative" role="alert">
|
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 mt-5 rounded relative" role="alert">
|
||||||
<strong className="font-bold pr-2" id="heading">Error</strong>
|
|
||||||
<span className="block sm:inline" id="message">{props.errorMessage}</span>
|
<span className="block sm:inline" id="message">{props.errorMessage}</span>
|
||||||
<span className="absolute top-0 bottom-0 right-0 px-4 py-3" onClick={props.onClose}>
|
<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"
|
<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">
|
viewBox="0 0 20 20">
|
||||||
<title>Close</title>
|
<title>{t('index:errorClose')}</title>
|
||||||
<path
|
<path
|
||||||
d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/>
|
d="M14.348 14.849a1.2 1.2 0 0 1-1.697 0L10 11.819l-2.651 3.029a1.2 1.2 0 1 1-1.697-1.697l2.758-3.15-2.759-3.152a1.2 1.2 0 1 1 1.697-1.697L10 8.183l2.651-3.031a1.2 1.2 0 1 1 1.697 1.697l-2.758 3.152 2.758 3.15a1.2 1.2 0 0 1 0 1.698z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import Card from "./Card";
|
import {saveAs} from 'file-saver';
|
||||||
import {saveAs} from 'file-saver'
|
|
||||||
import React, {FormEvent, useEffect, useRef, useState} from "react";
|
import React, {FormEvent, useEffect, useRef, useState} from "react";
|
||||||
import {BrowserQRCodeReader} from "@zxing/browser";
|
import {BrowserQRCodeReader} from "@zxing/browser";
|
||||||
import {Result} from "@zxing/library";
|
import {Result} from "@zxing/library";
|
||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
import Card from "./Card";
|
||||||
|
import Alert from "./Alert";
|
||||||
import {PayloadBody} from "../src/payload";
|
import {PayloadBody} from "../src/payload";
|
||||||
import {getPayloadBodyFromFile, getPayloadBodyFromQR} from "../src/process";
|
import {getPayloadBodyFromFile, getPayloadBodyFromQR} from "../src/process";
|
||||||
import {PassData} from "../src/pass";
|
import {PassData} from "../src/pass";
|
||||||
import Alert from "./Alert";
|
|
||||||
|
|
||||||
function Form(): JSX.Element {
|
function Form(): JSX.Element {
|
||||||
|
const { t } = useTranslation(['index', 'errors', 'common']);
|
||||||
|
|
||||||
// Whether camera is open or not
|
// Whether camera is open or not
|
||||||
const [isCameraOpen, setIsCameraOpen] = useState<boolean>(false);
|
const [isCameraOpen, setIsCameraOpen] = useState<boolean>(false);
|
||||||
@ -20,9 +24,15 @@ function Form(): JSX.Element {
|
|||||||
const [qrCode, setQrCode] = useState<Result>(undefined);
|
const [qrCode, setQrCode] = useState<Result>(undefined);
|
||||||
const [file, setFile] = useState<File>(undefined);
|
const [file, setFile] = useState<File>(undefined);
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string>(undefined);
|
const [errorMessage, _setErrorMessage] = useState<string>(undefined);
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// Check if there is a translation and replace message accordingly
|
||||||
|
const setErrorMessage = (message: string) => {
|
||||||
|
const translation = t('errors:'.concat(message));
|
||||||
|
_setErrorMessage(translation !== message ? translation : message);
|
||||||
|
};
|
||||||
|
|
||||||
// File Input ref
|
// File Input ref
|
||||||
const inputFile = useRef<HTMLInputElement>(undefined)
|
const inputFile = useRef<HTMLInputElement>(undefined)
|
||||||
|
|
||||||
@ -92,7 +102,7 @@ function Form(): JSX.Element {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
if (!file && !qrCode) {
|
if (!file && !qrCode) {
|
||||||
setErrorMessage("Please scan a QR Code, or select a file to scan")
|
setErrorMessage('noFileOrQrCode')
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -113,7 +123,7 @@ function Form(): JSX.Element {
|
|||||||
saveAs(passBlob, 'covid.pkpass');
|
saveAs(passBlob, 'covid.pkpass');
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setErrorMessage(e.toString());
|
setErrorMessage(e.message);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,25 +131,21 @@ function Form(): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<form className="space-y-5" id="form" onSubmit={addToWallet}>
|
<form className="space-y-5" id="form" onSubmit={addToWallet}>
|
||||||
<Card step="1" heading="Select Certificate" content={
|
<Card step="1" heading={t('index:selectCertificate')} content={
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<p>
|
<p>{t('index:selectCertificateDescription')}</p>
|
||||||
Please select the certificate screenshot or (scanned) PDF page, which you received from your
|
|
||||||
doctor, pharmacy, vaccination centre or online. Note that taking a picture does not work on
|
|
||||||
most devices yet.
|
|
||||||
</p>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={isCameraOpen ? hideCameraView : showCameraView}
|
onClick={isCameraOpen ? hideCameraView : showCameraView}
|
||||||
className="focus:outline-none h-20 bg-gray-500 hover:bg-gray-700 text-white font-semibold rounded-md">
|
className="focus:outline-none h-20 bg-gray-500 hover:bg-gray-700 text-white font-semibold rounded-md">
|
||||||
{isCameraOpen ? "Stop Camera" : "Start Camera"}
|
{isCameraOpen ? t('index:stopCamera') : t('index:startCamera')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={showFileDialog}
|
onClick={showFileDialog}
|
||||||
className="focus:outline-none h-20 bg-gray-500 hover:bg-gray-700 text-white font-semibold rounded-md">
|
className="focus:outline-none h-20 bg-gray-500 hover:bg-gray-700 text-white font-semibold rounded-md">
|
||||||
Open File (PDF, PNG)
|
{t('index:openFile')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -160,7 +166,7 @@ function Form(): JSX.Element {
|
|||||||
</svg>
|
</svg>
|
||||||
<span className="w-full truncate">
|
<span className="w-full truncate">
|
||||||
{
|
{
|
||||||
qrCode && 'Found QR Code!'
|
qrCode && t('index:foundQrCode')
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
file && file.name
|
file && file.name
|
||||||
@ -170,22 +176,20 @@ function Form(): JSX.Element {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}/>
|
}/>
|
||||||
<Card step="2" heading="Pick a Color" content={
|
<Card step="2" heading={t('index:pickColor')} content={
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<p>
|
<p>{t('index:pickColorDescription')}</p>
|
||||||
Pick a background color for your pass.
|
|
||||||
</p>
|
|
||||||
<div className="relative inline-block w-full">
|
<div className="relative inline-block w-full">
|
||||||
<select name="color" id="color"
|
<select name="color" id="color"
|
||||||
className="bg-gray-200 dark:bg-gray-900 focus:outline-none w-full h-10 pl-3 pr-6 text-base rounded-md appearance-none cursor-pointer">
|
className="bg-gray-200 dark:bg-gray-900 focus:outline-none w-full h-10 pl-3 pr-6 text-base rounded-md appearance-none cursor-pointer">
|
||||||
<option value="white">white</option>
|
<option value="white">{t('index:colorWhite')}</option>
|
||||||
<option value="black">black</option>
|
<option value="black">{t('index:colorBlack')}</option>
|
||||||
<option value="grey">grey</option>
|
<option value="grey">{t('index:colorGrey')}</option>
|
||||||
<option value="green">green</option>
|
<option value="green">{t('index:colorGreen')}</option>
|
||||||
<option value="indigo">indigo</option>
|
<option value="indigo">{t('index:colorIndigo')}</option>
|
||||||
<option value="blue">blue</option>
|
<option value="blue">{t('index:colorBlue')}</option>
|
||||||
<option value="purple">purple</option>
|
<option value="purple">{t('index:colorPurple')}</option>
|
||||||
<option value="teal">teal</option>
|
<option value="teal">{t('index:colorTeal')}</option>
|
||||||
</select>
|
</select>
|
||||||
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
|
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
|
||||||
<svg className="w-5 h-5 fill-current" viewBox="0 0 20 20">
|
<svg className="w-5 h-5 fill-current" viewBox="0 0 20 20">
|
||||||
@ -197,23 +201,31 @@ function Form(): JSX.Element {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}/>
|
}/>
|
||||||
<Card step="3" heading="Add to Wallet" content={
|
<Card step="3" heading={t('index:addToWallet')} content={
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<p>
|
<p>
|
||||||
Data privacy is of special importance when processing health-related data.
|
{t('index:dataPrivacyDescription')}
|
||||||
In order for you to make an informed decision, please read the <a href="/privacy">Privacy
|
<Link href="/privacy">
|
||||||
Policy</a>.
|
<a>
|
||||||
|
{t('index:privacyPolicy')}
|
||||||
|
</a>
|
||||||
|
</Link>.
|
||||||
</p>
|
</p>
|
||||||
<label htmlFor="privacy" className="flex flex-row space-x-4 items-center">
|
<label htmlFor="privacy" className="flex flex-row space-x-4 items-center">
|
||||||
<input type="checkbox" id="privacy" value="privacy" required className="h-4 w-4"/>
|
<input type="checkbox" id="privacy" value="privacy" required className="h-4 w-4"/>
|
||||||
<p>
|
<p>
|
||||||
I accept the <a href="/privacy" className="underline">Privacy Policy</a>
|
{t('index:iAcceptThe')}
|
||||||
|
<Link href="/privacy">
|
||||||
|
<a className="underline">
|
||||||
|
{t('index:privacyPolicy')}
|
||||||
|
</a>
|
||||||
|
</Link>.
|
||||||
</p>
|
</p>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-row items-center justify-start">
|
<div className="flex flex-row items-center justify-start">
|
||||||
<button id="download" type="submit"
|
<button id="download" type="submit"
|
||||||
className="focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
|
className="focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
|
||||||
Add to Wallet
|
{t('index:addToWallet')}
|
||||||
</button>
|
</button>
|
||||||
<div id="spin" className={loading ? undefined : "hidden"}>
|
<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-2" viewBox="0 0 24 24">
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
function Logo(): JSX.Element {
|
function Logo(): JSX.Element {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<a className="flex flex-row items-center p-3 justify-center space-x-1">
|
<a className="flex flex-row items-center p-3 justify-center space-x-1">
|
||||||
@ -16,7 +20,7 @@ function Logo(): JSX.Element {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<h1 className="text-3xl font-bold">
|
<h1 className="text-3xl font-bold">
|
||||||
CovidPass
|
{t('common:title')}
|
||||||
</h1>
|
</h1>
|
||||||
</a>
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import Logo from './Logo'
|
import Logo from './Logo'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@ -7,10 +9,12 @@ interface PageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Page(props: PageProps): JSX.Element {
|
function Page(props: PageProps): JSX.Element {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="md:w-2/3 xl:w-2/5 md:mx-auto flex flex-col min-h-screen justify-center px-5 py-12">
|
<div className="md:w-2/3 xl:w-2/5 md:mx-auto flex flex-col min-h-screen justify-center px-5 py-12">
|
||||||
<Head>
|
<Head>
|
||||||
<title>CovidPass</title>
|
<title>{t('common:title')}</title>
|
||||||
<link rel="icon" href="/favicon.ico"/>
|
<link rel="icon" href="/favicon.ico"/>
|
||||||
</Head>
|
</Head>
|
||||||
<div>
|
<div>
|
||||||
@ -20,11 +24,11 @@ function Page(props: PageProps): JSX.Element {
|
|||||||
{props.content}
|
{props.content}
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<nav className="nav flex pt-4 flex-row space-x-4 justify-center text-md font-bold">
|
<nav className="nav flex pt-4 flex-row space-x-4 justify-center text-md font-bold flex-wrap">
|
||||||
<a href="https://www.paypal.com/paypalme/msextro" className="hover:underline">Donate</a>
|
<a href="https://www.paypal.com/paypalme/msextro" className="hover:underline">{t('common:donate')}</a>
|
||||||
<a href="https://github.com/marvinsxtr/covidpass" className="hover:underline">GitHub</a>
|
<a href="https://github.com/marvinsxtr/covidpass" className="hover:underline">{t('common:gitHub')}</a>
|
||||||
<Link href="/privacy"><a className="hover:underline">Privacy Policy</a></Link>
|
<Link href="/privacy"><a className="hover:underline">{t('common:privacyPolicy')}</a></Link>
|
||||||
<Link href="/imprint"><a className="hover:underline">Imprint</a></Link>
|
<Link href="/imprint"><a className="hover:underline">{t('common:imprint')}</a></Link>
|
||||||
</nav>
|
</nav>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
|
9
next-i18next.config.js
Normal file
9
next-i18next.config.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
i18n: {
|
||||||
|
defaultLocale: 'en',
|
||||||
|
locales: ['en', 'de'],
|
||||||
|
localeExtension: 'yml',
|
||||||
|
defaultLanguage: 'en',
|
||||||
|
fallbackLng: ['en'],
|
||||||
|
},
|
||||||
|
};
|
5
next.config.js
Normal file
5
next.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const {i18n} = require('./next-i18next.config');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
i18n,
|
||||||
|
};
|
7926
package-lock.json
generated
7926
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@
|
|||||||
"jpeg-js": "^0.4.3",
|
"jpeg-js": "^0.4.3",
|
||||||
"jsqr": "^1.4.0",
|
"jsqr": "^1.4.0",
|
||||||
"next": "latest",
|
"next": "latest",
|
||||||
|
"next-i18next": "^8.5.1",
|
||||||
"next-seo": "^4.26.0",
|
"next-seo": "^4.26.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"pdfjs-dist": "^2.5.207",
|
"pdfjs-dist": "^2.5.207",
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import 'tailwindcss/tailwind.css'
|
import 'tailwindcss/tailwind.css';
|
||||||
|
|
||||||
import {DefaultSeo} from 'next-seo';
|
import {DefaultSeo} from 'next-seo';
|
||||||
import SEO from '../next-seo.config';
|
import SEO from '../next-seo.config';
|
||||||
import type {AppProps} from 'next/app'
|
import type {AppProps} from 'next/app';
|
||||||
|
import {appWithTranslation} from 'next-i18next';
|
||||||
|
|
||||||
function MyApp({Component, pageProps}: AppProps): JSX.Element {
|
function MyApp({Component, pageProps}: AppProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
@ -13,4 +14,4 @@ function MyApp({Component, pageProps}: AppProps): JSX.Element {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyApp;
|
export default appWithTranslation(MyApp);
|
@ -1,57 +1,39 @@
|
|||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
|
||||||
|
|
||||||
import Page from '../components/Page'
|
import Page from '../components/Page'
|
||||||
import Card from '../components/Card'
|
import Card from '../components/Card'
|
||||||
|
|
||||||
function Imprint(): JSX.Element {
|
function Imprint(): JSX.Element {
|
||||||
|
const { t } = useTranslation(['common', 'index', 'imprint']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page content={
|
<Page content={
|
||||||
<Card step="§" heading="Imprint" content={
|
<Card step="§" heading={t('common:imprint')} content={
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="font-bold">Information according to § 5 TMG</p>
|
<p className="font-bold">{t('imprint:heading')}</p>
|
||||||
<p>
|
<p>
|
||||||
Marvin Sextro<br/>
|
Marvin Sextro<br />
|
||||||
Wilhelm-Busch-Str. 8A<br/>
|
Wilhelm-Busch-Str. 8A<br />
|
||||||
30167 Hannover<br/>
|
30167 Hannover<br />
|
||||||
</p>
|
</p>
|
||||||
<p className="font-bold">Contact</p>
|
<p className="font-bold">{t('imprint:contact')}</p>
|
||||||
<p>
|
<p>
|
||||||
marvin.sextro@gmail.com
|
<a href="mailto:marvin.sextro@gmail.com" className="underline">marvin.sextro@gmail.com</a>
|
||||||
</p>
|
</p>
|
||||||
<p className="font-bold">EU Dispute Resolution</p>
|
<p className="font-bold">{t('imprint:euDisputeResolution')}</p>
|
||||||
|
<p>{t('imprint:euDisputeResolutionParagraph')}</p>
|
||||||
|
<p className="font-bold">{t('imprint:consumerDisputeResolution')}</p>
|
||||||
|
<p>{t('imprint:consumerDisputeResolutionParagraph')}</p>
|
||||||
|
<p className="font-bold">{t('imprint:liabilityForContents')}</p>
|
||||||
|
<p>{t('imprint:liabilityForContentsParagraph')}</p>
|
||||||
|
<p className="font-bold">{t('imprint:liabilityForLinks')}</p>
|
||||||
|
<p>{t('imprint:liabilityForLinksParagraph')}</p>
|
||||||
|
<p className="font-bold">{t('imprint:credits')}</p>
|
||||||
<p>
|
<p>
|
||||||
The European Commission provides a platform for online dispute resolution (OS): <a
|
{t('imprint:creditsSource')}
|
||||||
href="https://ec.europa.eu/consumers/odr"
|
<br />
|
||||||
className="underline">https://ec.europa.eu/consumers/odr</a>. You can find our e-mail address in
|
{t('imprint:creditsTranslation')}
|
||||||
the imprint above.
|
|
||||||
</p>
|
|
||||||
<p className="font-bold">Consumer dispute resolution / universal arbitration board</p>
|
|
||||||
<p>
|
|
||||||
We are not willing or obliged to participate in dispute resolution proceedings before a consumer
|
|
||||||
arbitration board.
|
|
||||||
</p>
|
|
||||||
<p className="font-bold">Liability for contents</p>
|
|
||||||
<p>
|
|
||||||
As a service provider, we are responsible for our own content on these pages in accordance with
|
|
||||||
§ 7 paragraph 1 TMG under the general laws. According to §§ 8 to 10 TMG, we are not obligated to
|
|
||||||
monitor transmitted or stored information or to investigate circumstances that indicate illegal
|
|
||||||
activity. Obligations to remove or block the use of information under the general laws remain
|
|
||||||
unaffected. However, liability in this regard is only possible from the point in time at which a
|
|
||||||
concrete infringement of the law becomes known. If we become aware of any such infringements, we
|
|
||||||
will remove the relevant content immediately.
|
|
||||||
</p>
|
|
||||||
<p className="font-bold">Liability for links</p>
|
|
||||||
<p>
|
|
||||||
Our offer contains links to external websites of third parties, on whose contents we have no
|
|
||||||
influence. Therefore, we cannot assume any liability for these external contents. The respective
|
|
||||||
provider or operator of the sites is always responsible for the content of the linked sites. The
|
|
||||||
linked pages were checked for possible legal violations at the time of linking. Illegal contents
|
|
||||||
were not recognizable at the time of linking. However, a permanent control of the contents of
|
|
||||||
the linked pages is not reasonable without concrete evidence of a violation of the law. If we
|
|
||||||
become aware of any infringements, we will remove such links immediately.
|
|
||||||
</p>
|
|
||||||
<p className="font-bold">Credits</p>
|
|
||||||
<p>
|
|
||||||
With excerpts from: https://www.e-recht24.de/impressum-generator.html
|
|
||||||
Translated with www.DeepL.com/Translator (free version)
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
}/>
|
}/>
|
||||||
@ -59,4 +41,12 @@ function Imprint(): JSX.Element {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStaticProps({ locale }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...(await serverSideTranslations(locale, ['index', 'imprint', 'common']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default Imprint;
|
export default Imprint;
|
@ -1,10 +1,14 @@
|
|||||||
import {NextSeo} from 'next-seo';
|
import {NextSeo} from 'next-seo';
|
||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
|
||||||
|
|
||||||
import Form from '../components/Form'
|
import Form from '../components/Form';
|
||||||
import Card from '../components/Card'
|
import Card from '../components/Card';
|
||||||
import Page from '../components/Page'
|
import Page from '../components/Page';
|
||||||
|
|
||||||
function Index(): JSX.Element {
|
function Index(): JSX.Element {
|
||||||
|
const { t } = useTranslation(['common', 'index', 'errors']);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NextSeo
|
<NextSeo
|
||||||
@ -33,10 +37,7 @@ function Index(): JSX.Element {
|
|||||||
<Page content={
|
<Page content={
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<Card content={
|
<Card content={
|
||||||
<p>
|
<p>{t('common:subtitle')} {t('index:iosHint')}</p>
|
||||||
Add your EU Digital Covid Vaccination Certificates to your favorite wallet app. On iOS,
|
|
||||||
please use the Safari Browser.
|
|
||||||
</p>
|
|
||||||
}/>
|
}/>
|
||||||
|
|
||||||
<Form/>
|
<Form/>
|
||||||
@ -46,4 +47,12 @@ function Index(): JSX.Element {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStaticProps({ locale }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...(await serverSideTranslations(locale, ['common', 'index', 'errors'])),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default Index;
|
export default Index;
|
||||||
|
@ -1,222 +1,162 @@
|
|||||||
|
import {useTranslation} from 'next-i18next';
|
||||||
|
import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
|
||||||
|
|
||||||
import Page from '../components/Page'
|
import Page from '../components/Page'
|
||||||
import Card from '../components/Card'
|
import Card from '../components/Card'
|
||||||
|
|
||||||
function Privacy(): JSX.Element {
|
function Privacy(): JSX.Element {
|
||||||
|
const { t } = useTranslation(['common', 'index', 'privacy']);
|
||||||
return (
|
return (
|
||||||
<Page content={
|
<Page content={
|
||||||
<Card step="i" heading="Privacy Policy" content={
|
<Card step="i" heading={t('common:privacyPolicy')} content={
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p>
|
<p>{t('privacy:gdprNotice')}</p>
|
||||||
Our privacy policy is based on the terms used by the European legislator for the adoption of the
|
<p className="font-bold">{t('privacy:generalInfo')}</p>
|
||||||
General Data Protection Regulation (GDPR).
|
|
||||||
</p>
|
|
||||||
<p className="font-bold">General information</p>
|
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
|
<li>{t('privacy:generalInfoProcess')}</li>
|
||||||
|
<li>{t('privacy:generalInfoStoring')}</li>
|
||||||
|
<li>{t('privacy:generalInfoThirdParties')}</li>
|
||||||
|
<li>{t('privacy:generalInfoHttps')}</li>
|
||||||
|
<li>{t('privacy:generalInfoLocation')}</li>
|
||||||
<li>
|
<li>
|
||||||
The whole process of generating the pass file happens locally in your browser. For the
|
{t('privacy:generalInfoGitHub')}
|
||||||
signing step, only a hashed representation of your data is sent to the server.
|
|
||||||
|
<a href="https://github.com/marvinsxtr/covidpass" className="underline">
|
||||||
|
GitHub
|
||||||
|
</a>.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Your data is not stored beyond the active browser session and the site does not use
|
{t('privacy:generalInfoLockScreen')}
|
||||||
cookies.
|
|
||||||
|
<a href="https://support.apple.com/guide/iphone/control-access-information-lock-screen-iph9a2a69136/ios" className="underline">
|
||||||
|
{t('privacy:settings')}
|
||||||
|
</a>.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
No data is sent to third parties.
|
{t('privacy:generalInfoProvider')}
|
||||||
</li>
|
|
||||||
<li>
|
<a href="https://www.hetzner.com/de/rechtliches/datenschutz/" className="underline">
|
||||||
We transmit your data securely over https.
|
{t('privacy:privacyPolicy')}
|
||||||
</li>
|
</a>
|
||||||
<li>
|
|
||||||
Our server is hosted in Nuremberg, Germany.
|
{t('privacy:andThe')}
|
||||||
</li>
|
|
||||||
<li>
|
<a href="https://docs.hetzner.com/general/general-terms-and-conditions/data-privacy-faq/privacy.tsx" className="underline">
|
||||||
The source code of this site is available on <a
|
{t('privacy:dataPrivacyFaq')}
|
||||||
href="https://github.com/marvinsxtr/covidpass" className="underline">GitHub</a>.
|
</a>.
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
By default, Apple Wallet passes are accessible from the lock screen. This can be changed
|
|
||||||
in the <a href="https://support.apple.com/de-de/guide/iphone/iph9a2a69136/ios"
|
|
||||||
className="underline">settings</a>.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
The server provider processes data to provide this site. In order to better understand
|
|
||||||
what measures they take to protect your data, please also read their <a
|
|
||||||
href="https://www.hetzner.com/de/rechtliches/datenschutz/" className="underline">privacy
|
|
||||||
policy</a> and the <a
|
|
||||||
href="https://docs.hetzner.com/general/general-terms-and-conditions/data-privacy-faq/privacy.tsx"
|
|
||||||
className="underline">data privacy FAQ</a>.
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-bold">Contact</p>
|
<p className="font-bold">{t('privacy:contact')}</p>
|
||||||
<p>
|
<p>
|
||||||
Marvin Sextro<br/>
|
Marvin Sextro<br/>
|
||||||
Wilhelm-Busch-Str. 8A<br/>
|
Wilhelm-Busch-Str. 8A<br/>
|
||||||
30167 Hannover<br/>
|
30167 Hannover<br/>
|
||||||
Germany<br/>
|
{t('privacy:email')}:
|
||||||
Email: marvin.sextro@gmail.com<br/>
|
|
||||||
Website: <a href="https://marvinsextro.de"
|
<a href="mailto:marvin.sextro@gmail.com">marvin.sextro@gmail.com</a>
|
||||||
className="underline">https://marvinsextro.de</a><br/>
|
<br/>
|
||||||
</p>
|
{t('privacy:website')}:
|
||||||
<p className="font-bold">Simplified explanation of the process</p>
|
|
||||||
<p>
|
<a href="https://marvinsextro.de" className="underline">https://marvinsextro.de</a>
|
||||||
This process is only started after accepting this policy and clicking on the Add to Wallet
|
|
||||||
button.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
First, the following steps happen locally in your browser:
|
|
||||||
</p>
|
</p>
|
||||||
|
<p className="font-bold">{t('privacy:process')}</p>
|
||||||
|
<p>{t('privacy:processFirst')}:</p>
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
<li>Recognizing and extracting the QR code data from your selected certificate</li>
|
<li>{t('privacy:processRecognizing')}</li>
|
||||||
<li>Decoding your personal and health-related data from the QR code payload</li>
|
<li>{t('privacy:processDecoding')}</li>
|
||||||
<li>Assembling an incomplete pass file out of your data</li>
|
<li>{t('privacy:processAssembling')}</li>
|
||||||
<li>Generating a file containing hashes of the data stored in the pass file</li>
|
<li>{t('privacy:processGenerating')}</li>
|
||||||
<li>Sending only the file containing the hashes to our server</li>
|
<li>{t('privacy:processSending')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>{t('privacy:processSecond')}:</p>
|
||||||
Second, the following steps happen on our server:
|
|
||||||
</p>
|
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
<li>Receiving and checking the hashes which were generated locally</li>
|
<li>{t('privacy:processReceiving')}</li>
|
||||||
<li>Signing the file containing the hashes</li>
|
<li>{t('privacy:processSigning')}</li>
|
||||||
<li>Sending the signature back</li>
|
<li>{t('privacy:processSendingBack')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>{t('privacy:processThird')}:</p>
|
||||||
Finally, the following steps happen locally in your browser:
|
|
||||||
</p>
|
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
<li>Assembling the signed pass file out of the incomplete file generated locally and the
|
<li>{t('privacy:processCompleting')}</li>
|
||||||
signature
|
<li>{t('privacy:processSaving')}</li>
|
||||||
</li>
|
|
||||||
<li>Saving the file on your device</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-bold">Locally processed data</p>
|
<p className="font-bold">{t('privacy:locallyProcessedData')}</p>
|
||||||
<p>
|
<p>
|
||||||
The following data is processed on in your browser to generate the pass file.
|
{t('privacy:the')}
|
||||||
|
|
||||||
|
<a href="https://github.com/ehn-dcc-development/ehn-dcc-schema" className="underline">
|
||||||
|
{t('privacy:schema')}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{t('privacy:specification')}
|
||||||
</p>
|
</p>
|
||||||
|
<p className="font-bold">{t('privacy:serverProvider')}</p>
|
||||||
|
<p>{t('privacy:serverProviderIs')}</p>
|
||||||
<p>
|
<p>
|
||||||
Processed personal data contained in the QR code:
|
<a href="https://www.hetzner.com/" className="underline">
|
||||||
|
Hetzner Online GmbH
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
Industriestr. 25<br />
|
||||||
|
91710 Gunzenhausen<br />
|
||||||
</p>
|
</p>
|
||||||
|
<p>{t('privacy:logFiles')}:</p>
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
<li>Your first and last name</li>
|
<li>{t('privacy:logFilesBrowser')}</li>
|
||||||
<li>Your date of birth</li>
|
<li>{t('privacy:logFilesOs')}</li>
|
||||||
|
<li>{t('privacy:logFilesReferrer')}</li>
|
||||||
|
<li>{t('privacy:logFilesTime')}</li>
|
||||||
|
<li>{t('privacy:logFilesIpAddress')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p className="font-bold">{t('privacy:rights')}</p>
|
||||||
For each vaccination certificate contained in the QR code, the following data is processed:
|
<p>{t('privacy:rightsGranted')}:</p>
|
||||||
</p>
|
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
<li>Targeted disease</li>
|
<li>{t('privacy:rightsAccess')}</li>
|
||||||
<li>Vaccine medical product</li>
|
<li>{t('privacy:rightsErasure')}</li>
|
||||||
<li>Manufacturer/Marketing Authorization Holder</li>
|
<li>{t('privacy:rightsRectification')}</li>
|
||||||
<li>Dose number</li>
|
<li>{t('privacy:rightsPortability')}</li>
|
||||||
<li>Total series of doses</li>
|
|
||||||
<li>Date of vaccination</li>
|
|
||||||
<li>Country of vaccination</li>
|
|
||||||
<li>Certificate issuer</li>
|
|
||||||
<li>Unique certificate identifier (UVCI)</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p className="font-bold">{t('privacy:thirdParties')}</p>
|
||||||
For each test certificate contained in the QR code, the following data is processed:
|
|
||||||
</p>
|
|
||||||
<div className="px-4">
|
|
||||||
<ul className="list-disc">
|
|
||||||
<li>Targeted disease</li>
|
|
||||||
<li>Test type</li>
|
|
||||||
<li>NAA Test name</li>
|
|
||||||
<li>RAT Test name and manufacturer</li>
|
|
||||||
<li>Date/Time of Sample Collection</li>
|
|
||||||
<li>Test Result</li>
|
|
||||||
<li>Testing Centre</li>
|
|
||||||
<li>Country of test</li>
|
|
||||||
<li>Certificate Issuer</li>
|
|
||||||
<li>Unique Certificate Identifier (UVCI)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
For each recovery certificate contained in the QR code, the following data is processed:
|
|
||||||
</p>
|
|
||||||
<div className="px-4">
|
|
||||||
<ul className="list-disc">
|
|
||||||
<li>Targeted disease</li>
|
|
||||||
<li>Date of first positive NAA test result</li>
|
|
||||||
<li>Country of test</li>
|
|
||||||
<li>Certificate Issuer</li>
|
|
||||||
<li>Certificate valid from</li>
|
|
||||||
<li>Certificate valid until</li>
|
|
||||||
<li>Unique Certificate Identifier (UVCI)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
The <a href="https://github.com/ehn-dcc-development/ehn-dcc-schema" className="underline">Digital
|
|
||||||
Covid Certificate Schema</a> contains a detailed specification of which data can be contained in
|
|
||||||
the QR code.
|
|
||||||
</p>
|
|
||||||
<p className="font-bold">Server provider</p>
|
|
||||||
<p>
|
|
||||||
Our server provider is <a href="https://www.hetzner.com/" className="underline">Hetzner Online
|
|
||||||
GmbH</a>.
|
|
||||||
The following data may be collected and stored in the server log files:
|
|
||||||
</p>
|
|
||||||
<div className="px-4">
|
|
||||||
<ul className="list-disc">
|
|
||||||
<li>The browser types and versions used</li>
|
|
||||||
<li>The operating system used by the accessing system</li>
|
|
||||||
<li>The website from which an accessing system reaches our website (so-called referrers)
|
|
||||||
</li>
|
|
||||||
<li>The date and time of access</li>
|
|
||||||
<li>The pseudonymised IP addresses</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<p className="font-bold">Your rights</p>
|
|
||||||
In accordance with the GDPR you have the following rights:
|
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
<li>
|
<li>
|
||||||
Right of access to your data: You have the right to know what data has been collected
|
GitHub:
|
||||||
about you and how it was processed.
|
|
||||||
|
<a href="https://docs.github.com/en/github/site-policy/github-privacy-statement" className="underline">
|
||||||
|
{t('common:privacyPolicy')}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Right to be forgotten: Erasure of your personal data.
|
PayPal:
|
||||||
|
|
||||||
|
<a href="https://www.paypal.com/de/webapps/mpp/ua/privacy-full?locale.x=en_EN" className="underline">
|
||||||
|
{t('common:privacyPolicy')}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Right of rectification: You have the right to correct inaccurate data.
|
Gmail/Google:
|
||||||
|
|
||||||
|
<a href="https://policies.google.com/privacy?hl=en-US" className="underline">
|
||||||
|
{t('common:privacyPolicy')}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Right of data portability: You have the right to transfer your data from one processing
|
{t('privacy:appleSync')}:
|
||||||
system into another.
|
|
||||||
</li>
|
<a href="https://www.apple.com/legal/privacy/en-ww/privacy.tsx" className="underline">
|
||||||
</ul>
|
{t('common:privacyPolicy')}
|
||||||
</div>
|
</a>
|
||||||
<p className="font-bold">Third parties linked</p>
|
|
||||||
<div className="px-4">
|
|
||||||
<ul className="list-disc">
|
|
||||||
<li>
|
|
||||||
GitHub: <a href="https://docs.github.com/en/github/site-policy/github-privacy-statement"
|
|
||||||
className="underline">Privacy Policy</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
PayPal: <a href="https://www.paypal.com/de/webapps/mpp/ua/privacy-full?locale.x=en_EN"
|
|
||||||
className="underline">Privacy Policy</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Gmail/Google: <a href="https://policies.google.com/privacy?hl=en-US"
|
|
||||||
className="underline">Privacy Policy</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Apple may sync your passes via iCloud: <a
|
|
||||||
href="https://www.apple.com/legal/privacy/en-ww/privacy.tsx" className="underline">Privacy
|
|
||||||
Policy</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -226,4 +166,12 @@ function Privacy(): JSX.Element {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStaticProps({ locale }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
...(await serverSideTranslations(locale, ['index', 'privacy', 'common'])),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default Privacy;
|
export default Privacy;
|
6
public/locales/de/common.yml
Normal file
6
public/locales/de/common.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
title: CovidPass
|
||||||
|
subtitle: Übertrage Deine digitalen EU COVID-Impfzertifikate in Deine Wallet-Apps.
|
||||||
|
privacyPolicy: Datenschutz
|
||||||
|
donate: Unterstützen
|
||||||
|
gitHub: GitHub
|
||||||
|
imprint: Impressum
|
14
public/locales/de/errors.yml
Normal file
14
public/locales/de/errors.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
noFileOrQrCode: Bitte scanne einen QR-Code oder wähle eine Datei aus
|
||||||
|
signatureFailed: Fehler beim Signieren der Karte auf dem Server
|
||||||
|
decodingFailed: Dekodierung der QR-Code-Daten fehlgeschlagen
|
||||||
|
invalidColor: Ungültige Farbe
|
||||||
|
vaccinationInfo: Impfinformationen konnten nicht gelesen werden
|
||||||
|
nameMissing: Name konnte nicht gelesen werden
|
||||||
|
dobMissing: Geburtsdatum konnte nicht gelesen werden
|
||||||
|
invalidMedicalProduct: Ungültiges Medizinprodukt
|
||||||
|
invalidCountryCode: Ungültiger Ländercode
|
||||||
|
invalidManufacturer: Ungültiger Hersteller
|
||||||
|
invalidFileType: Ungültiger Dateityp
|
||||||
|
couldNotDecode: Dekodierung aus QR-Code fehlgeschlagen
|
||||||
|
couldNotFindQrCode: QR-Code konnte in der ausgewählten Datei nicht gefunden werden
|
||||||
|
invalidQrCode: Ungültiger QR-Code
|
31
public/locales/de/imprint.yml
Normal file
31
public/locales/de/imprint.yml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
heading: Angaben gemäß § 5 TMG
|
||||||
|
contact: Kontakt
|
||||||
|
euDisputeResolution: EU-Streitschlichtung
|
||||||
|
euDisputeResolutionParagraph: |
|
||||||
|
Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: https://ec.europa.eu/consumers/odr.
|
||||||
|
Unsere E-Mail-Adresse finden Sie oben im Impressum.
|
||||||
|
consumerDisputeResolution: Verbraucherstreitbeilegung / Universalschlichtungsstelle
|
||||||
|
consumerDisputeResolutionParagraph: Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.
|
||||||
|
liabilityForContents: Haftung für Inhalte
|
||||||
|
liabilityForContentsParagraph: |
|
||||||
|
Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den
|
||||||
|
allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht
|
||||||
|
verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu
|
||||||
|
forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
|
||||||
|
Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen
|
||||||
|
Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der
|
||||||
|
Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden
|
||||||
|
Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
|
||||||
|
liabilityForLinks: Haftung für Links
|
||||||
|
liabilityForLinksParagraph: |
|
||||||
|
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss haben.
|
||||||
|
Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der
|
||||||
|
verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten
|
||||||
|
Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte
|
||||||
|
waren zum Zeitpunkt der Verlinkung nicht erkennbar.
|
||||||
|
Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer
|
||||||
|
Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links
|
||||||
|
umgehend entfernen.
|
||||||
|
credits: Quellen
|
||||||
|
creditsSource: Mit Auszügen aus https://www.e-recht24.de/impressum-generator.html
|
||||||
|
creditsTranslation: Übersetzt mit https://www.DeepL.com/Translator (kostenlose Version)
|
26
public/locales/de/index.yml
Normal file
26
public/locales/de/index.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
iosHint: Bitte verwende unter iOS den Safari Browser.
|
||||||
|
errorClose: Schließen
|
||||||
|
selectCertificate: Zertifikat auswählen
|
||||||
|
selectCertificateDescription: |
|
||||||
|
Scanne den QR-Code auf Deinem Zertifikat oder wähle einen Screenshot oder eine PDF-Datei mit dem QR-Code.
|
||||||
|
Bitte beachte, dass die Auswahl einer Datei direkt von der Kamera nicht unterstützt wird.
|
||||||
|
stopCamera: Kamera stoppen
|
||||||
|
startCamera: Kamera starten
|
||||||
|
openFile: Datei auswählen (PDF, PNG)
|
||||||
|
foundQrCode: QR-Code gefunden!
|
||||||
|
pickColor: Farbe auswählen
|
||||||
|
pickColorDescription: Wähle eine Hintergrundfarbe für Deine Karte.
|
||||||
|
colorWhite: weiß
|
||||||
|
colorBlack: schwarz
|
||||||
|
colorGrey: grau
|
||||||
|
colorGreen: grün
|
||||||
|
colorIndigo: indigo
|
||||||
|
colorBlue: blau
|
||||||
|
colorPurple: lila
|
||||||
|
colorTeal: blaugrün
|
||||||
|
addToWallet: Zu Wallet hinzufügen
|
||||||
|
dataPrivacyDescription: |
|
||||||
|
Datenschutz hat einen besonders hohen Stellenwert, wenn es um die Verarbeitung von gesundheitsbezogenen Daten geht.
|
||||||
|
Damit Du eine fundierte Entscheidung triffst, lies bitte die
|
||||||
|
iAcceptThe: Ich akzeptiere die
|
||||||
|
privacyPolicy: Datenschutzerklärung
|
56
public/locales/de/privacy.yml
Normal file
56
public/locales/de/privacy.yml
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
gdprNotice: |
|
||||||
|
Unsere Datenschutzerklärung orientiert sich an den Vorgaben des europäischen Gesetzgebers für die Umsetzung der Datenschutz-Grundverordnung (DSGVO).
|
||||||
|
generalInfo: Generelle Informationen
|
||||||
|
generalInfoProcess: |
|
||||||
|
Der gesamte Prozess der Erstellung der Karte findet lokal im Browser statt.
|
||||||
|
Für den Schritt des Signierens wird nur eine gehashte Repräsentation der Daten an den Server geschickt.
|
||||||
|
generalInfoStoring: Ihre Daten werden nicht über die aktive Browsersitzung hinaus gespeichert und es werden keine Cookies verwendet.
|
||||||
|
generalInfoThirdParties: Keine Daten werden an Dritte weitergeleitet.
|
||||||
|
generalInfoHttps: Wir übertragen Ihre Daten sicher über https.
|
||||||
|
generalInfoLocation: Unser Server befindet sich in Nürnberg.
|
||||||
|
generalInfoGitHub: Der Quellcode dieser Seite ist öffentlich verfügbar auf
|
||||||
|
generalInfoLockScreen: Standardmäßig sind Apple Wallet Karten vom Sperrbildschirm erreichbar. Um dies zu ändern, gehen Sie in die
|
||||||
|
settings: Einstellungen
|
||||||
|
generalInfoProvider: |
|
||||||
|
Der Server-Anbieter verarbeitet Daten, um diese Seite bereitzustellen.
|
||||||
|
Um besser zu verstehen, welche Maßnahmen der Anbieter ergreift, um ihre Daten zu schützen, lesen Sie bitte auch die
|
||||||
|
privacyPolicy: Datenschutzerklärung
|
||||||
|
andThe: und das
|
||||||
|
dataPrivacyFaq: Datenschutz FAQ
|
||||||
|
contact: Kontakt
|
||||||
|
email: E-Mail
|
||||||
|
website: Webseite
|
||||||
|
process: Vereinfachte Erklärung des Prozesses
|
||||||
|
processFirst: Die folgenden Schritte werden zunächst in Ihrem Browser durchgeführt
|
||||||
|
processSecond: Dann werden die folgenden Schritte auf unserem Server abgearbeitet
|
||||||
|
processThird: Zuletzt werden die folgenden Schritte in Ihrem Browser durchgeführt
|
||||||
|
processRecognizing: Erkennung und Extraktion der QR-Code Daten aus dem von Ihnen gewählten Zertifikat
|
||||||
|
processDecoding: Dekodierung Ihrer persönlichen und gesundheitsbezogenen Daten aus den QR-Code Daten
|
||||||
|
processAssembling: Erstellung einer unvollständigen Karten-Datei aus Ihren Daten
|
||||||
|
processGenerating: Generierung einer Datei, welche Hashes der Daten aus der Karten-Datei beinhaltet
|
||||||
|
processSending: Senden der Datei mit den Hashes an unseren Server
|
||||||
|
processReceiving: Empfangen und kontrollieren der lokal generierten Hashes
|
||||||
|
processSigning: Signieren der Datei, welche die Hashes beinhaltet
|
||||||
|
processSendingBack: Zurücksenden der Signatur
|
||||||
|
processCompleting: Erstellung der signierten Karten-Datei aus der unvollständigen lokal generierten Datei und der Signatur
|
||||||
|
processSaving: Speichern der Datei auf dem Endgerät
|
||||||
|
locallyProcessedData: Lokal verarbeitete Daten
|
||||||
|
the: Das
|
||||||
|
schema: Digital Covid Certificate Schema
|
||||||
|
specification: enthält eine detaillierte Spezifikation darüber, welche Daten in dem QR-Code enthalten sind und lokal verarbeitet werden können.
|
||||||
|
serverProvider: Server-Anbieter
|
||||||
|
serverProviderIs: Unser Server-Anbieter ist
|
||||||
|
logFiles: Die folgenden Daten können gesammelt und in den Server Log-Dateien gespeichert werden
|
||||||
|
logFilesBrowser: Browser Typ und Version
|
||||||
|
logFilesOs: Verwendetes Betriebssystem
|
||||||
|
logFilesReferrer: Die Website von der unsere Seite erreicht wird (sog. Referrer)
|
||||||
|
logFilesTime: Das Datum und die Uhrzeit des Zugriffs
|
||||||
|
logFilesIpAddress: Die pseudonymisierten IP-Adressen
|
||||||
|
rights: Ihre Rechte
|
||||||
|
rightsGranted: In Übereinstimmung mit der DSGVO haben Sie die folgenden Rechte
|
||||||
|
rightsAccess: Das Recht auf Zugriff auf Ihre Daten; Sie haben das Recht, zu erfahren welche Daten über Sie gesammelt wurden und wie diese verarbeitet wurden.
|
||||||
|
rightsErasure: Das Recht, vergessen zu werden; Löschung Ihrer persönlichen Daten.
|
||||||
|
rightsRectification: Recht der Richtigstellung; Sie haben das Recht, inkorrekte Daten zu korrigieren.
|
||||||
|
rightsPortability: Recht auf Portabilität; Sie haben das Recht, Ihre Daten von einem informationsverarbeitenden System in ein anderes zu übertragen.
|
||||||
|
thirdParties: Verlinkte Dritte
|
||||||
|
appleSync: Apple kann Ihre Apple Wallet Karten über iCloud synchronisieren
|
6
public/locales/en/common.yml
Normal file
6
public/locales/en/common.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
title: CovidPass
|
||||||
|
subtitle: Add your EU Digital Covid Vaccination Certificates to your favorite wallet apps.
|
||||||
|
privacyPolicy: Privacy Policy
|
||||||
|
donate: Sponsor
|
||||||
|
gitHub: GitHub
|
||||||
|
imprint: Imprint
|
14
public/locales/en/errors.yml
Normal file
14
public/locales/en/errors.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
noFileOrQrCode: Please scan a QR Code, or select a file
|
||||||
|
signatureFailed: Error while signing pass on server
|
||||||
|
decodingFailed: Failed to decode QR code payload
|
||||||
|
invalidColor: Invalid color
|
||||||
|
vaccinationInfo: Failed to read vaccination information
|
||||||
|
nameMissing: Failed to read name
|
||||||
|
dobMissing: Failed to read date of birth
|
||||||
|
invalidMedicalProduct: Invalid medical product
|
||||||
|
invalidCountryCode: Invalid country code
|
||||||
|
invalidManufacturer: Invalid manufacturer
|
||||||
|
invalidFileType: Invalid file type
|
||||||
|
couldNotDecode: Could not decode QR code from file
|
||||||
|
couldNotFindQrCode: Could not find QR Code in provided file
|
||||||
|
invalidQrCode: Invalid QR code
|
27
public/locales/en/imprint.yml
Normal file
27
public/locales/en/imprint.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
heading: Information according to § 5 TMG
|
||||||
|
contact: Contact
|
||||||
|
euDisputeResolution: EU Dispute Resolution
|
||||||
|
euDisputeResolutionParagraph: |
|
||||||
|
The European Commission provides a platform for online dispute resolution (OS) https://ec.europa.eu/consumers/odr.
|
||||||
|
You can find our e-mail address in the imprint above.
|
||||||
|
consumerDisputeResolution: Consumer dispute resolution / universal arbitration board
|
||||||
|
consumerDisputeResolutionParagraph: We are not willing or obliged to participate in dispute resolution proceedings before a consumer arbitration board.
|
||||||
|
liabilityForContents: Liability for contents
|
||||||
|
liabilityForContentsParagraph: |
|
||||||
|
As a service provider, we are responsible for our own content on these pages in accordance with § 7 paragraph 1 TMG under the general laws.
|
||||||
|
According to §§ 8 to 10 TMG, we are not obligated to monitor transmitted or stored information or to investigate circumstances that indicate illegal activity.
|
||||||
|
Obligations to remove or block the use of information under the general laws remain unaffected.
|
||||||
|
However, liability in this regard is only possible from the point in time at which a concrete infringement of the law becomes known.
|
||||||
|
If we become aware of any such infringements, we will remove the relevant content immediately.
|
||||||
|
liabilityForLinks: Liability for links
|
||||||
|
liabilityForLinksParagraph: |
|
||||||
|
Our offer contains links to external websites of third parties, on whose contents we have no influence.
|
||||||
|
Therefore, we cannot assume any liability for these external contents.
|
||||||
|
The respective provider or operator of the sites is always responsible for the content of the linked sites.
|
||||||
|
The linked pages were checked for possible legal violations at the time of linking.
|
||||||
|
Illegal contents were not recognizable at the time of linking.
|
||||||
|
However, a permanent control of the contents of the linked pages is not reasonable without concrete evidence of a violation of the law.
|
||||||
|
If we become aware of any infringements, we will remove such links immediately.
|
||||||
|
credits: Credits
|
||||||
|
creditsSource: With excerpts from https://www.e-recht24.de/impressum-generator.html
|
||||||
|
creditsTranslation: Translated with https://www.DeepL.com/Translator (free version)
|
26
public/locales/en/index.yml
Normal file
26
public/locales/en/index.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
iosHint: On iOS, please use the Safari Browser.
|
||||||
|
errorClose: Close
|
||||||
|
selectCertificate: Select Certificate
|
||||||
|
selectCertificateDescription: |
|
||||||
|
Please scan the QR code on your certificate or select a screenshot or PDF page with the QR code.
|
||||||
|
Note that selecting a file directly from camera is not supported.
|
||||||
|
stopCamera: Stop Camera
|
||||||
|
startCamera: Start Camera
|
||||||
|
openFile: Select File (PDF, PNG)
|
||||||
|
foundQrCode: Found QR Code!
|
||||||
|
pickColor: Pick a Color
|
||||||
|
pickColorDescription: Pick a background color for your pass.
|
||||||
|
colorWhite: white
|
||||||
|
colorBlack: black
|
||||||
|
colorGrey: grey
|
||||||
|
colorGreen: green
|
||||||
|
colorIndigo: indigo
|
||||||
|
colorBlue: blue
|
||||||
|
colorPurple: purple
|
||||||
|
colorTeal: teal
|
||||||
|
addToWallet: Add to Wallet
|
||||||
|
dataPrivacyDescription: |
|
||||||
|
Data privacy is of special importance when processing health-related data.
|
||||||
|
In order for you to make an informed decision, please read the
|
||||||
|
iAcceptThe: I accept the
|
||||||
|
privacyPolicy: Privacy Policy
|
57
public/locales/en/privacy.yml
Normal file
57
public/locales/en/privacy.yml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
gdprNotice: |
|
||||||
|
Our privacy policy is based on the terms used by the European legislator
|
||||||
|
for the adoption of the General Data Protection Regulation (GDPR).
|
||||||
|
generalInfo: General information
|
||||||
|
generalInfoProcess: |
|
||||||
|
The whole process of generating the pass file happens locally in your browser.
|
||||||
|
For the signing step, only a hashed representation of your data is sent to the server.
|
||||||
|
generalInfoStoring: Your data is not stored beyond the active browser session and the site does not use cookies.
|
||||||
|
generalInfoThirdParties: No data is sent to third parties.
|
||||||
|
generalInfoHttps: We transmit your data securely over https.
|
||||||
|
generalInfoLocation: Our server is hosted in Nuremberg, Germany.
|
||||||
|
generalInfoGitHub: The source code of this site is available on
|
||||||
|
generalInfoLockScreen: By default, Apple Wallet passes are accessible from the lock screen. This can be changed in the
|
||||||
|
settings: settings
|
||||||
|
generalInfoProvider: |
|
||||||
|
The server provider processes data to provide this site.
|
||||||
|
In order to better understand what measures they take to protect your data, please also read their
|
||||||
|
privacyPolicy: privacy policy
|
||||||
|
andThe: and the
|
||||||
|
dataPrivacyFaq: data privacy FAQ
|
||||||
|
contact: Contact
|
||||||
|
email: Email
|
||||||
|
website: Website
|
||||||
|
process: Simplified 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
|
||||||
|
processRecognizing: Recognizing and extracting the QR code data from your selected certificate
|
||||||
|
processDecoding: Decoding your personal and health-related data from the QR code payload
|
||||||
|
processAssembling: Assembling an incomplete pass file out of your data
|
||||||
|
processGenerating: Generating a file containing hashes of the data stored in the pass file
|
||||||
|
processSending: Sending only the file containing the hashes to our server
|
||||||
|
processReceiving: Receiving and checking the hashes which were generated locally
|
||||||
|
processSigning: Signing the file containing the hashes
|
||||||
|
processSendingBack: Sending the signature back
|
||||||
|
processCompleting: Assembling the signed pass file out of the incomplete file generated locally and the signature
|
||||||
|
processSaving: Saving the file on your device
|
||||||
|
locallyProcessedData: Locally processed data
|
||||||
|
the: The
|
||||||
|
schema: Digital Covid Certificate Schema
|
||||||
|
specification: contains a detailed specification of which data can be contained in the QR code and will be processed in your browser.
|
||||||
|
serverProvider: Server provider
|
||||||
|
serverProviderIs: Our server provider is
|
||||||
|
logFiles: The following data may be collected and stored in the server log files
|
||||||
|
logFilesBrowser: The browser types and versions used
|
||||||
|
logFilesOs: The operating system used by the accessing system
|
||||||
|
logFilesReferrer: The website from which an accessing system reaches our website (so-called referrers)
|
||||||
|
logFilesTime: The date and time of access
|
||||||
|
logFilesIpAddress: The pseudonymised IP addresses
|
||||||
|
rights: Your rights
|
||||||
|
rightsGranted: In accordance with the GDPR you have the following rights
|
||||||
|
rightsAccess: Right of access to your data; You have the right to know what data has been collected about you and how it was processed.
|
||||||
|
rightsErasure: Right to be forgotten; Erasure of your personal data.
|
||||||
|
rightsRectification: Right of rectification; You have the right to correct inaccurate data.
|
||||||
|
rightsPortability: Right of data portability; You have the right to transfer your data from one processing system into another.
|
||||||
|
thirdParties: Third parties linked
|
||||||
|
appleSync: Apple may sync your passes via iCloud
|
@ -26,11 +26,7 @@ export function decodeData(data: string): Object {
|
|||||||
|
|
||||||
if (data.startsWith(':')) {
|
if (data.startsWith(':')) {
|
||||||
data = data.substring(1);
|
data = data.substring(1);
|
||||||
} else {
|
|
||||||
console.log("Warning: unsafe HC1: header");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log("Warning: no HC1: header");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var arrayBuffer: Uint8Array = base45.decode(data);
|
var arrayBuffer: Uint8Array = base45.decode(data);
|
||||||
@ -41,12 +37,8 @@ export function decodeData(data: string): Object {
|
|||||||
|
|
||||||
var payloadArray: Array<Uint8Array> = cbor.decode(typedArrayToBuffer(arrayBuffer));
|
var payloadArray: Array<Uint8Array> = cbor.decode(typedArrayToBuffer(arrayBuffer));
|
||||||
|
|
||||||
if (!Array.isArray(payloadArray)) {
|
if (!Array.isArray(payloadArray) || payloadArray.length !== 4) {
|
||||||
throw new Error('Expecting Array');
|
throw new Error('decodingFailed');
|
||||||
}
|
|
||||||
|
|
||||||
if (payloadArray.length !== 4) {
|
|
||||||
throw new Error('Expecting Array of length 4');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var plaintext: Uint8Array = payloadArray[2];
|
var plaintext: Uint8Array = payloadArray[2];
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import {toBuffer as createZip} from 'do-not-zip';
|
||||||
|
import {v4 as uuid4} from 'uuid';
|
||||||
|
|
||||||
import {Constants} from "./constants";
|
import {Constants} from "./constants";
|
||||||
import {Payload, PayloadBody} from "./payload";
|
import {Payload, PayloadBody} from "./payload";
|
||||||
import {ValueSets} from "./value_sets";
|
import {ValueSets} from "./value_sets";
|
||||||
import {toBuffer as createZip} from 'do-not-zip';
|
|
||||||
import {v4 as uuid4} from 'uuid';
|
|
||||||
|
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ export class PassData {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw Error("Error while singing Pass on server")
|
throw Error('signatureFailed')
|
||||||
}
|
}
|
||||||
|
|
||||||
return await response.arrayBuffer()
|
return await response.arrayBuffer()
|
||||||
|
@ -36,7 +36,7 @@ export class Payload {
|
|||||||
let colors = Constants.COLORS;
|
let colors = Constants.COLORS;
|
||||||
|
|
||||||
if (!(body.color in colors)) {
|
if (!(body.color in colors)) {
|
||||||
throw new Error('Invalid color');
|
throw new Error('invalidColor');
|
||||||
}
|
}
|
||||||
|
|
||||||
const dark = body.color != 'white'
|
const dark = body.color != 'white'
|
||||||
@ -48,15 +48,15 @@ export class Payload {
|
|||||||
const dateOfBirthInformation = body.decodedData['-260']['1']['dob'];
|
const dateOfBirthInformation = body.decodedData['-260']['1']['dob'];
|
||||||
|
|
||||||
if (vaccinationInformation == undefined) {
|
if (vaccinationInformation == undefined) {
|
||||||
throw new Error('Failed to read vaccination information');
|
throw new Error('vaccinationInfo');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nameInformation == undefined) {
|
if (nameInformation == undefined) {
|
||||||
throw new Error('Failed to read name');
|
throw new Error('nameMissing');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dateOfBirthInformation == undefined) {
|
if (dateOfBirthInformation == undefined) {
|
||||||
throw new Error('Failed to read date of birth');
|
throw new Error('dobMissing');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Medical, country and manufacturer information
|
// Get Medical, country and manufacturer information
|
||||||
@ -65,13 +65,13 @@ export class Payload {
|
|||||||
const manufacturerKey = vaccinationInformation['ma'];
|
const manufacturerKey = vaccinationInformation['ma'];
|
||||||
|
|
||||||
if (!(medialProductKey in valueSets.medicalProducts)) {
|
if (!(medialProductKey in valueSets.medicalProducts)) {
|
||||||
throw new Error('Invalid medical product key');
|
throw new Error('invalidMedicalProduct');
|
||||||
}
|
}
|
||||||
if (!(countryCode in valueSets.countryCodes)) {
|
if (!(countryCode in valueSets.countryCodes)) {
|
||||||
throw new Error('Invalid country code')
|
throw new Error('invalidCountryCode')
|
||||||
}
|
}
|
||||||
if (!(manufacturerKey in valueSets.manufacturers)) {
|
if (!(manufacturerKey in valueSets.manufacturers)) {
|
||||||
throw new Error('Invalid manufacturer')
|
throw new Error('invalidManufacturer')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,7 +91,6 @@ export class Payload {
|
|||||||
this.dateOfBirth = dateOfBirthInformation;
|
this.dateOfBirth = dateOfBirthInformation;
|
||||||
this.uvci = vaccinationInformation['ci'];
|
this.uvci = vaccinationInformation['ci'];
|
||||||
this.certificateIssuer = vaccinationInformation['is'];
|
this.certificateIssuer = vaccinationInformation['is'];
|
||||||
this.medicalProductKey = medialProductKey; // TODO is this needed?
|
|
||||||
|
|
||||||
this.countryOfVaccination = valueSets.countryCodes[countryCode].display;
|
this.countryOfVaccination = valueSets.countryCodes[countryCode].display;
|
||||||
this.vaccineName = valueSets.medicalProducts[medialProductKey].display;
|
this.vaccineName = valueSets.medicalProducts[medialProductKey].display;
|
||||||
|
@ -23,7 +23,7 @@ export async function getPayloadBodyFromFile(file: File, color: string): Promise
|
|||||||
imageData = await getImageDataFromPng(fileBuffer)
|
imageData = await getImageDataFromPng(fileBuffer)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw Error('Invalid File Type')
|
throw Error('invalidFileType')
|
||||||
}
|
}
|
||||||
|
|
||||||
let code: QRCode;
|
let code: QRCode;
|
||||||
@ -33,11 +33,11 @@ export async function getPayloadBodyFromFile(file: File, color: string): Promise
|
|||||||
inversionAttempts: "dontInvert",
|
inversionAttempts: "dontInvert",
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Error("Could not decode QR Code from File");
|
throw Error('couldNotDecode');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code == undefined) {
|
if (code == undefined) {
|
||||||
throw Error("Could not find QR Code in provided File")
|
throw Error('couldNotFindQrCode')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get raw data
|
// Get raw data
|
||||||
@ -49,7 +49,7 @@ export async function getPayloadBodyFromFile(file: File, color: string): Promise
|
|||||||
try {
|
try {
|
||||||
decodedData = decodeData(rawData);
|
decodedData = decodeData(rawData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Error("Invalid QR Code")
|
throw Error('invalidQrCode')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -70,7 +70,7 @@ export async function getPayloadBodyFromQR(qrCodeResult: Result, color: string):
|
|||||||
try {
|
try {
|
||||||
decodedData = decodeData(rawData);
|
decodedData = decodeData(rawData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Error("Invalid QR Code")
|
throw Error("invalidQrCode")
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user