Compare commits
8 Commits
59e079f27e
...
1cbaccdf82
Author | SHA1 | Date |
---|---|---|
Marvin Sextro | 1cbaccdf82 | |
Marvin Sextro | d1da98a9e4 | |
Marvin Sextro | 1eb6bea1a6 | |
Marvin Sextro | c106be369f | |
Marvin Sextro | bc3efc4728 | |
Marvin Sextro | b349653721 | |
Marvin Sextro | 47cd07a59d | |
Julien Blatecky | 674d325502 |
|
@ -1,5 +1,5 @@
|
||||||
# Install dependencies only when needed
|
# Install dependencies only when needed
|
||||||
FROM node:14-alpine AS deps
|
FROM node:16-alpine AS deps
|
||||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||||
RUN apk add --no-cache libc6-compat
|
RUN apk add --no-cache libc6-compat
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
@ -7,14 +7,14 @@ COPY package.json ./
|
||||||
RUN yarn install --frozen-lockfile
|
RUN yarn install --frozen-lockfile
|
||||||
|
|
||||||
# Rebuild the source code only when needed
|
# Rebuild the source code only when needed
|
||||||
FROM node:14-alpine AS builder
|
FROM node:16-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 yarn 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:16-alpine AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
|
|
|
@ -1,18 +1,36 @@
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
text?: string,
|
text?: string,
|
||||||
icon?: string,
|
icon?: string,
|
||||||
onClick: () => void,
|
onClick?: () => void,
|
||||||
|
loading?: boolean,
|
||||||
|
type?: ButtonType,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ButtonType {
|
||||||
|
submit = 'submit',
|
||||||
|
button = 'button',
|
||||||
}
|
}
|
||||||
|
|
||||||
function Button(props: ButtonProps): JSX.Element {
|
function Button(props: ButtonProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type={props.type}
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
className="focus:outline-none h-20 bg-gray-400 dark:bg-gray-600 hover:bg-gray-500 text-white font-semibold rounded-md items-center flex justify-center">
|
className={`${props.type == ButtonType.submit ? "bg-green-600 hover:bg-green-700" : "bg-gray-400 dark:bg-gray-600 hover:bg-gray-500"} relative focus:outline-none h-20 text-white font-semibold rounded-md items-center flex justify-center`}>
|
||||||
{
|
{
|
||||||
props.icon && <img src={props.icon} className="w-12 h-12 mr-2 -ml-4" />
|
props.icon && <img src={props.icon} className="w-12 h-12 mr-2 -ml-4" />
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
props.type == ButtonType.submit &&
|
||||||
|
<div id="spin" className={`${props.loading ? undefined : "hidden"} absolute left-2`}>
|
||||||
|
<svg className="animate-spin h-5 w-5 ml-4" viewBox="0 0 24 24">
|
||||||
|
<circle className="opacity-0" cx="12" cy="12" r="10" stroke="currentColor"
|
||||||
|
strokeWidth="4"/>
|
||||||
|
<path className="opacity-80" fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
{props.text}
|
{props.text}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {getPayloadBodyFromFile, getPayloadBodyFromQR} from "../src/process";
|
||||||
import {PassData} from "../src/pass";
|
import {PassData} from "../src/pass";
|
||||||
import {COLORS} from "../src/colors";
|
import {COLORS} from "../src/colors";
|
||||||
import Colors from './Colors';
|
import Colors from './Colors';
|
||||||
import Button from './Button';
|
import Button, { ButtonType } from './Button';
|
||||||
|
|
||||||
function Form(): JSX.Element {
|
function Form(): JSX.Element {
|
||||||
const {t} = useTranslation(['index', 'errors', 'common']);
|
const {t} = useTranslation(['index', 'errors', 'common']);
|
||||||
|
@ -274,19 +274,8 @@ function Form(): JSX.Element {
|
||||||
</Link>.
|
</Link>.
|
||||||
</p>
|
</p>
|
||||||
</label>
|
</label>
|
||||||
<div className="flex flex-row items-center justify-start">
|
<div className="grid grid-cols-1">
|
||||||
<button id="download" type="submit"
|
<Button type={ButtonType.submit} text={t('index:addToWallet')} loading={loading} />
|
||||||
className="focus:outline-none bg-green-600 py-2 px-3 text-white font-semibold rounded-md disabled:bg-gray-400">
|
|
||||||
{t('index:addToWallet')}
|
|
||||||
</button>
|
|
||||||
<div id="spin" className={loading ? undefined : "hidden"}>
|
|
||||||
<svg className="animate-spin h-5 w-5 ml-4" 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"
|
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}/>
|
}/>
|
||||||
|
|
|
@ -12,7 +12,7 @@ function Page(props: PageProps): JSX.Element {
|
||||||
const { t } = useTranslation('common');
|
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 pt-12 pb-16">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{t('common:title')}</title>
|
<title>{t('common:title')}</title>
|
||||||
<link rel="icon" href="/favicon.ico"/>
|
<link rel="icon" href="/favicon.ico"/>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
title: CovidPass
|
title: CovidPass
|
||||||
subtitle: Lägg till digitala Covid-certifikat från EU i din favoritplånboksapp.
|
subtitle: Lägg till digitala Covid-certifikat från EU i din favorit plånboks app.
|
||||||
privacyPolicy: Integritetspolicy
|
privacyPolicy: Integritetspolicy
|
||||||
donate: Sponsra
|
donate: Sponsra
|
||||||
gitHub: GitHub
|
gitHub: GitHub
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
gdprNotice: |
|
gdprNotice: |
|
||||||
Vår integritetspolicy är baserad på de villkår som används av den europeiska lagstiftaren för verkställandet av den allmänna dataskyddsförordningen (GDPR).
|
Vår integritetspolicy är baserad på de villkår som används av den europeiska lagstiftaren för verkställandet av den allmänna dataskyddsförordningen (GDPR).
|
||||||
generalInfo: Allmän information
|
generalInfo: Allmän information
|
||||||
generalInfoProcess: |
|
generalInfoProcess: |
|
||||||
Hela den process som genererar passfilen sker lokalt i din webbläsare.
|
Hela den process som genererar passfilen sker lokalt i din webbläsare.
|
||||||
Under signeringen skickas endast en kondenserad motsvarighet av din data till servern.
|
Under signeringen skickas endast en kondenserad motsvarighet av din data till servern.
|
||||||
generalInfoStoring: Din data sparas inte utanför den aktiva webbläsarsessionen och sidan använder sig inte av cookies.
|
generalInfoStoring: Din data sparas inte utanför den aktiva webbläsar sessionen och sidan använder sig inte av cookies.
|
||||||
generalInfoThirdParties: Ingen data skickas till tredjeparter.
|
generalInfoThirdParties: Ingen data skickas till tredjeparter.
|
||||||
generalInfoHttps: Vi överför din data säkert över HTTPS.
|
generalInfoHttps: Vi överför din data säkert över HTTPS.
|
||||||
generalInfoLocation: Vår server är baserad i Nürnberg, Tyskland.
|
generalInfoLocation: Vår server är baserad i Nürnberg, Tyskland.
|
||||||
|
|
|
@ -20,6 +20,10 @@ export function typedArrayToBuffer(array: Uint8Array): ArrayBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function decodeData(data: string): Object {
|
export function decodeData(data: string): Object {
|
||||||
|
if (data.startsWith('https://')) {
|
||||||
|
var url = new URL(data);
|
||||||
|
data = decodeURIComponent(url.hash.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
if (data.startsWith('HC1')) {
|
if (data.startsWith('HC1')) {
|
||||||
data = data.substring(3);
|
data = data.substring(3);
|
||||||
|
@ -45,4 +49,4 @@ export function decodeData(data: string): Object {
|
||||||
var decoded: Object = cbor.decode(typedArrayToBufferSliced(plaintext));
|
var decoded: Object = cbor.decode(typedArrayToBufferSliced(plaintext));
|
||||||
|
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ interface QrCode {
|
||||||
message: string;
|
message: string;
|
||||||
format: QrFormat;
|
format: QrFormat;
|
||||||
messageEncoding: Encoding;
|
messageEncoding: Encoding;
|
||||||
|
altText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SignData {
|
interface SignData {
|
||||||
|
@ -83,6 +84,7 @@ export class PassData {
|
||||||
message: payload.rawData,
|
message: payload.rawData,
|
||||||
format: QrFormat.PKBarcodeFormatQR,
|
format: QrFormat.PKBarcodeFormatQR,
|
||||||
messageEncoding: Encoding.utf8,
|
messageEncoding: Encoding.utf8,
|
||||||
|
altText: 'SCAN TO VERIFY',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create pass data
|
// Create pass data
|
||||||
|
|
|
@ -235,7 +235,17 @@ export class Payload {
|
||||||
throw new Error('invalidTestType')
|
throw new Error('invalidTestType')
|
||||||
}
|
}
|
||||||
|
|
||||||
const testResult = valueSets.testResults[testResultKey].display;
|
let testResult = valueSets.testResults[testResultKey].display;
|
||||||
|
|
||||||
|
switch (testResult) {
|
||||||
|
case 'Not detected':
|
||||||
|
testResult = 'Negative';
|
||||||
|
break;
|
||||||
|
case 'Detected':
|
||||||
|
testResult = 'Positive';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const testType = valueSets.testTypes[testTypeKey].display;
|
const testType = valueSets.testTypes[testTypeKey].display;
|
||||||
|
|
||||||
const testTime = testDateTimeString.replace(/.*T/, '').replace('Z', ' ') + 'UTC';
|
const testTime = testDateTimeString.replace(/.*T/, '').replace('Z', ' ') + 'UTC';
|
||||||
|
@ -339,6 +349,14 @@ export class Payload {
|
||||||
throw new Error('certificateType');
|
throw new Error('certificateType');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.backFields.push(...[
|
||||||
|
{
|
||||||
|
key: "credits",
|
||||||
|
label: "",
|
||||||
|
value: "Created with <a href='https://covidpass.marvinsextro.de'>CovidPass</a>"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,6 @@ export async function getPayloadBodyFromFile(file: File, color: COLORS): Promise
|
||||||
|
|
||||||
switch (file.type) {
|
switch (file.type) {
|
||||||
case 'application/pdf':
|
case 'application/pdf':
|
||||||
console.log('pdf')
|
|
||||||
// Read file
|
// Read file
|
||||||
const fileBuffer = await file.arrayBuffer();
|
const fileBuffer = await file.arrayBuffer();
|
||||||
imageData = await getImageDataFromPdf(fileBuffer)
|
imageData = await getImageDataFromPdf(fileBuffer)
|
||||||
|
@ -21,7 +20,6 @@ export async function getPayloadBodyFromFile(file: File, color: COLORS): Promise
|
||||||
case 'image/jpeg':
|
case 'image/jpeg':
|
||||||
case 'image/webp':
|
case 'image/webp':
|
||||||
case 'image/gif':
|
case 'image/gif':
|
||||||
console.log(`image ${file.type}`)
|
|
||||||
imageData = await getImageDataFromImage(file)
|
imageData = await getImageDataFromImage(file)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -209,14 +209,14 @@
|
||||||
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
|
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
|
||||||
|
|
||||||
"@types/node@*":
|
"@types/node@*":
|
||||||
version "17.0.2"
|
version "17.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.2.tgz#a4c07d47ff737e8ee7e586fe636ff0e1ddff070a"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.4.tgz#fec0ce0526abb6062fd206d72a642811b887a111"
|
||||||
integrity sha512-JepeIUPFDARgIs0zD/SKPgFsJEAF0X5/qO80llx59gOxFTboS9Amv3S+QfB7lqBId5sFXJ99BN0J6zFRvL9dDA==
|
integrity sha512-6xwbrW4JJiJLgF+zNypN5wr2ykM9/jHcL7rQ8fZe2vuftggjzZeRSM4OwRc6Xk8qWjwJ99qVHo/JgOGmomWRog==
|
||||||
|
|
||||||
"@types/pako@^1.0.1":
|
"@types/pako@^1.0.1":
|
||||||
version "1.0.2"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.2.tgz#17c9b136877f33d9ecc8e73cd26944f1f6dd39a1"
|
resolved "https://registry.yarnpkg.com/@types/pako/-/pako-1.0.3.tgz#2e61c2b02020b5f44e2e5e946dfac74f4ec33c58"
|
||||||
integrity sha512-8UJl2MjkqqS6ncpLZqRZ5LmGiFBkbYxocD4e4jmBqGvfRG1RS23gKsBQbdtV9O9GvRyjFTiRHRByjSlKCLlmZw==
|
integrity sha512-EDxOsHAD5dqjbjEUM1xwa7rpKPFb8ECBE5irONTQU7/OsO3thI5YrNEWSPNMvYmvFM0l/OLQJ6Mgw7PEdXSjhg==
|
||||||
|
|
||||||
"@types/parse-json@^4.0.0":
|
"@types/parse-json@^4.0.0":
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
@ -229,9 +229,9 @@
|
||||||
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^17.0.11":
|
"@types/react@*", "@types/react@^17.0.11":
|
||||||
version "17.0.37"
|
version "17.0.38"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd"
|
||||||
integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg==
|
integrity sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
|
@ -889,9 +889,9 @@ convert-source-map@1.7.0:
|
||||||
safe-buffer "~5.1.1"
|
safe-buffer "~5.1.1"
|
||||||
|
|
||||||
core-js@^3:
|
core-js@^3:
|
||||||
version "3.20.0"
|
version "3.20.1"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.20.0.tgz#1c5ac07986b8d15473ab192e45a2e115a4a95b79"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.20.1.tgz#eb1598047b7813572f1dc24b7c6a95528c99eef3"
|
||||||
integrity sha512-KjbKU7UEfg4YPpskMtMXPhUKn7m/1OdTHTVjy09ScR2LVaoUXe8Jh0UdvN2EKUR6iKTJph52SJP95mAB0MnVLQ==
|
integrity sha512-btdpStYFQScnNVQ5slVcr858KP0YWYjV16eGJQw8Gg7CWtu/2qNvIM3qVRIR3n1pK2R9NNOrTevbvAYxajwEjg==
|
||||||
|
|
||||||
core-util-is@~1.0.0:
|
core-util-is@~1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
|
@ -1077,9 +1077,9 @@ domain-browser@^1.1.1:
|
||||||
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
|
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
|
||||||
|
|
||||||
electron-to-chromium@^1.3.723, electron-to-chromium@^1.4.17:
|
electron-to-chromium@^1.3.723, electron-to-chromium@^1.4.17:
|
||||||
version "1.4.27"
|
version "1.4.28"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.27.tgz#bfc6e798d8a56a17d658312f4b7ae1a7ca87724f"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.28.tgz#fef0e92e281df6d568f482d8d53c34ca5374de48"
|
||||||
integrity sha512-uZ95szi3zUbzRDx1zx/xnsCG+2xgZyy57pDOeaeO4r8zx5Dqe8Jv1ti8cunvBwJHVI5LzPuw8umKwZb3WKYxSQ==
|
integrity sha512-Gzbf0wUtKfyPaqf0Plz+Ctinf9eQIzxEqBHwSvbGfeOm9GMNdLxyu1dNiCUfM+x6r4BE0xUJNh3Nmg9gfAtTmg==
|
||||||
|
|
||||||
elliptic@^6.5.3:
|
elliptic@^6.5.3:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
|
|
Loading…
Reference in New Issue