1.8 finally working
This commit is contained in:
parent
37d8ed99e4
commit
13a07de0e0
|
@ -1,2 +1,3 @@
|
|||
API_BASE_URL=https://covidpassapinet-pnrnxf7lvq-pd.a.run.app
|
||||
VERIFIER_HOST=https://verifier.vaccine-ontario.ca
|
||||
HITCOUNT_HOST=https://hitcount.vaccine-ontario.ca
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
docker build . -t covidpass -t gcr.io/broadcast2patients/covidpass2
|
||||
docker build . -t covidpass2 -t gcr.io/broadcast2patients/covidpass2
|
||||
docker push gcr.io/broadcast2patients/covidpass2
|
||||
gcloud config set project broadcast2patients
|
||||
gcloud config set run/region us-east1
|
||||
gcloud run deploy covidpass2 --image gcr.io/broadcast2patients/covidpass2:latest --platform managed
|
||||
|
|
|
@ -16,6 +16,7 @@ import {COLORS} from "../src/colors";
|
|||
import Colors from './Colors';
|
||||
import {isChrome, isIOS, isIPad13, isMacOs, isSafari, deviceDetect, osName, osVersion} from 'react-device-detect';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { counterReset } from 'html2canvas/dist/types/css/property-descriptors/counter-reset';
|
||||
|
||||
|
||||
function Form(): JSX.Element {
|
||||
|
@ -37,6 +38,42 @@ function Form(): JSX.Element {
|
|||
const [errorMessage, _setErrorMessage] = useState<string>(undefined);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
const [passCount, setPassCount] = useState<string>('');
|
||||
const [generated, setGenerated] = useState<boolean>(false); // this flag represents the file has been used to generate a pass
|
||||
const hitcountHost = 'https://stats.vaccine-ontario.ca';
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (passCount.length == 0) {
|
||||
getPassCount();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getPassCount = async () => {
|
||||
const hitCount = await getHitCount();
|
||||
console.log(`hitcount = ${hitCount}`);
|
||||
setPassCount(hitCount);
|
||||
};
|
||||
|
||||
async function getHitCount() {
|
||||
|
||||
try {
|
||||
const request = `${hitcountHost}/nocount?url=pass.vaccine-ontario.ca`;
|
||||
|
||||
let response = await fetch(request);
|
||||
const counter = await response.text();
|
||||
|
||||
console.log('getHitCount returns ' + counter);
|
||||
|
||||
return Promise.resolve(counter);
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if there is a translation and replace message accordingly
|
||||
const setErrorMessage = (message: string) => {
|
||||
if (message == undefined) {
|
||||
|
@ -59,6 +96,7 @@ function Form(): JSX.Element {
|
|||
if (selectedFile !== undefined) {
|
||||
setQrCode(undefined);
|
||||
setFile(selectedFile);
|
||||
setGenerated(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -132,6 +170,34 @@ function Form(): JSX.Element {
|
|||
setIsCameraOpen(true);
|
||||
}
|
||||
|
||||
async function incrementCount() {
|
||||
|
||||
try {
|
||||
if (typeof generated == undefined || !generated) {
|
||||
|
||||
const request = `${hitcountHost}/count?url=pass.vaccine-ontario.ca`;
|
||||
console.log(request);
|
||||
|
||||
let response = await fetch(request);
|
||||
console.log(request);
|
||||
|
||||
const counter = await response.text(); // response count is not used intentionally so it always goes up by 1 only even if the server has changed
|
||||
|
||||
let newPasscount = Number(passCount) + 1;
|
||||
console.log(counter);
|
||||
setPassCount(counter);
|
||||
setGenerated(true);
|
||||
console.log(`new PassCount = ${newPasscount}`);
|
||||
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add Pass to wallet
|
||||
async function addToWallet(event: FormEvent<HTMLFormElement>) {
|
||||
|
||||
|
@ -153,6 +219,8 @@ function Form(): JSX.Element {
|
|||
console.log('> generatePass');
|
||||
|
||||
payloadBody = await getPayloadBodyFromFile(file, color);
|
||||
await incrementCount();
|
||||
|
||||
let pass = await PassData.generatePass(payloadBody);
|
||||
const passBlob = new Blob([pass], {type: "application/vnd.apple.pkpass"});
|
||||
saveAs(passBlob, 'covid.pkpass');
|
||||
|
@ -161,12 +229,15 @@ function Form(): JSX.Element {
|
|||
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setErrorMessage(e.message);
|
||||
Sentry.captureException(e);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: merge with addToWallet for common flow
|
||||
|
||||
async function saveAsPhoto() {
|
||||
|
||||
setLoading(true);
|
||||
|
@ -181,6 +252,8 @@ function Form(): JSX.Element {
|
|||
|
||||
try {
|
||||
payloadBody = await getPayloadBodyFromFile(file, null);
|
||||
await incrementCount();
|
||||
|
||||
let photoBlob = await Photo.generatePass(payloadBody);
|
||||
saveAs(photoBlob, 'pass.png');
|
||||
|
||||
|
@ -210,12 +283,12 @@ function Form(): JSX.Element {
|
|||
document.getElementById('download').setAttribute('disabled','true');
|
||||
}
|
||||
if (isIOS && (!osVersion.includes('13') && !osVersion.includes('14') && !osVersion.includes('15'))) {
|
||||
setErrorMessage(`Sorry. iOS 13+ is needed for the Apple Wallet functionality to work (Your version is ${osVersion})`);
|
||||
document.getElementById('download').setAttribute('disabled','true');
|
||||
setErrorMessage('Sorry, iOS 13+ is needed for the Apple Wallet functionality to work')
|
||||
document.getElementById('download').setAttribute('disabled','true')
|
||||
}
|
||||
if (isIOS && !isSafari) {
|
||||
setErrorMessage(`Sorry. Only Safari can be used to add a Wallet Pass on iOS`);
|
||||
document.getElementById('download').setAttribute('disabled','true');
|
||||
setErrorMessage('Sorry, only Safari can be used to add a Wallet Pass on iOS')
|
||||
document.getElementById('download').setAttribute('disabled','true')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,6 +318,7 @@ function Form(): JSX.Element {
|
|||
<Card step="2" heading={t('index:selectCertificate')} content={
|
||||
<div className="space-y-5">
|
||||
<p>{t('index:selectCertificateDescription')}</p>
|
||||
<p>{t('index:selectCertificateReminder')}</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<button
|
||||
type="button"
|
||||
|
@ -296,6 +370,7 @@ function Form(): JSX.Element {
|
|||
<Check text={t('qrCode')}/>
|
||||
<Check text={t('openSourceTransparent')}/>
|
||||
<Check text={t('verifierLink')}/>
|
||||
{passCount && <Check text={passCount + ' ' + t('numPasses')}/>}
|
||||
|
||||
{/* <Check text={t('hostedInEU')}/> */}
|
||||
</ul>
|
||||
|
|
|
@ -31,10 +31,10 @@ function Page(props: PageProps): JSX.Element {
|
|||
<a href="https://twitter.com/grassroots_team" className="underline">{t('index:whatsnew')}</a>
|
||||
<a href="mailto:grassroots@vaccine-ontario.ca" className="underline">{t('common:contact')}</a>
|
||||
<a href="https://verifier.vaccine-ontario.ca" className="underline">{t('common:gotoVerifier')}</a>
|
||||
<a href="https://github.com/billylo1/covidpass" className="underline">{t('common:gitHub')}</a>
|
||||
<a href="https://vaccine-ontario.ca" className="underline">{t('common:returnToMainSite')}</a>
|
||||
{/* <a href="https://github.com/billylo1/covidpass" className="hover:underline">{t('common:gitHub')}</a> */}
|
||||
</nav>
|
||||
<div className="flex pt-4 flex-row space-x-4 justify-center text-md flex-wrap">Last updated: 2021-09-14 (v1.7)</div>
|
||||
<div className="flex pt-4 flex-row space-x-4 justify-center text-md flex-wrap">Last updated: 2021-09-18 (v1.8)</div>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
const {i18n} = require('./next-i18next.config');
|
||||
|
||||
module.exports = {
|
||||
i18n,
|
||||
i18n, async headers() {
|
||||
return [
|
||||
{
|
||||
// matching all API routes
|
||||
source: "/api/:path*",
|
||||
headers: [
|
||||
{ key: "Access-Control-Allow-Credentials", value: "true" },
|
||||
{ key: "Access-Control-Allow-Origin", value: "*" },
|
||||
{ key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
|
||||
{ key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "grassroots_covidpass",
|
||||
"version": "1.7.0",
|
||||
"version": "1.8.0",
|
||||
"author": "Billy Lo <billy@vaccine-ontario.ca>",
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
|
|
@ -3,11 +3,12 @@ import type {NextApiRequest, NextApiResponse} from "next";
|
|||
type ConfigData = {
|
||||
apiBaseUrl: string
|
||||
verifierHost: string
|
||||
hitcountHost: string
|
||||
}
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse<ConfigData>) {
|
||||
// Return the API_BASE_URL. This Endpoint allows us to access the env Variable in client javascript
|
||||
|
||||
res.status(200).json({apiBaseUrl: process.env.API_BASE_URL, verifierHost: process.env.VERIFIER_HOST})
|
||||
res.status(200).json({apiBaseUrl: process.env.API_BASE_URL, verifierHost: process.env.VERIFIER_HOST, hitcountHost: process.env.HITCOUNT_HOST})
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
|
|||
import Form from '../components/Form';
|
||||
import Card from '../components/Card';
|
||||
import Page from '../components/Page';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
function Index(): JSX.Element {
|
||||
const { t } = useTranslation(['common', 'index', 'errors']);
|
||||
|
@ -40,7 +41,7 @@ function Index(): JSX.Element {
|
|||
<Page content={
|
||||
<div className="space-y-5">
|
||||
<Card content={
|
||||
<p>{t('common:subtitle')}</p>
|
||||
<div><p>{t('common:subtitle')}</p><br /><p>{t('common:subtitle2')}</p></div>
|
||||
}/>
|
||||
|
||||
<Form/>
|
||||
|
@ -50,6 +51,7 @@ function Index(): JSX.Element {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
title: Vaccination Receipt to Wallet
|
||||
subtitle: This tool converts your vaccination receipt from Ontario Ministry of Health to an Apple Wallet pass with a QR code. Other organizations (e.g. schools, workplaces) can then scan that QR code to validate in a privacy-respecting way.
|
||||
verifySubtitle: Confirm vaccination record is consistent with provincial records
|
||||
subtitle: This tool converts your vaccination receipt from Ontario Ministry of Health to an Apple Wallet pass for easy access in the interim.
|
||||
subtitle2: Once Ontario's official QR code is released on Oct 22, you will be able to update your Apple Wallet pass by visiting this site again.
|
||||
privacyPolicy: Privacy Policy
|
||||
donate: Sponsor
|
||||
gitHub: GitHub
|
||||
|
|
|
@ -2,7 +2,9 @@ iosHint: On iOS, please use Safari.
|
|||
errorClose: Close
|
||||
selectCertificate: Select vaccination receipt (PDF)
|
||||
selectCertificateDescription: |
|
||||
Press "Select File", "Browse..." and select the PDF file you have saved in Step 1. [ Reminder : Only receipts downloaded from the provincial web site can be verified and converted to Apple Wallet Pass.]
|
||||
Press "Select File", "Browse..." and select the PDF file you have saved in Step 1.
|
||||
selectCertificateReminder: |
|
||||
Reminder : Receipts directly downloaded from the provincial web site is required. Emailed copies are not digitally signed and cannot be added to Apple Wallet.
|
||||
#stopCamera: Stop Camera
|
||||
#startCamera: Start Camera
|
||||
openFile: Select File
|
||||
|
@ -23,7 +25,7 @@ colorBlue: blue
|
|||
colorPurple: purple
|
||||
colorTeal: teal
|
||||
addToWallet: Add to Apple Wallet
|
||||
addToWalletHeader: Add to Apple Wallet / Save as Photos
|
||||
addToWalletHeader: Add to Apple Wallet / Save as Photo
|
||||
saveAsPhoto: Save as Photo
|
||||
dataPrivacyDescription: |
|
||||
Press the "Add to Wallet" below to import data into Wallet.
|
||||
|
@ -33,6 +35,7 @@ createdOnDevice: No personal data is sent to the Internet.
|
|||
qrCode: QR code is for verification only, with no personal info.
|
||||
openSourceTransparent: Source code is free and open for re-use/contributions on GitHub.
|
||||
verifierLink: QR code verifier available at https://verifier.vaccine-ontario.ca
|
||||
numPasses: receipts processed since Sept 2, 2021
|
||||
demo: Video Demo
|
||||
whatsnew: What's New
|
||||
#hostedInEU: Hosted in the EU
|
|
@ -56,11 +56,13 @@ export class PassData {
|
|||
private static async signWithRemote(signData: SignData): Promise<ArrayBuffer> {
|
||||
// Load API_BASE_URL form nextjs backend
|
||||
|
||||
// console.log('signWithRemote');
|
||||
|
||||
const configResponse = await fetch('/api/config')
|
||||
const apiBaseUrl = (await configResponse.json()).apiBaseUrl
|
||||
console.log(`${apiBaseUrl}/sign`);
|
||||
|
||||
console.log(JSON.stringify(signData));
|
||||
// console.log(JSON.stringify(signData));
|
||||
|
||||
const response = await fetch(`${apiBaseUrl}/sign`, {
|
||||
method: 'POST',
|
||||
|
@ -111,7 +113,7 @@ export class PassData {
|
|||
const response = await fetch(`${verifierHost}/register`, requestOptions);
|
||||
const responseJson = await response.json();
|
||||
|
||||
console.log(JSON.stringify(responseJson,null,2));
|
||||
// console.log(JSON.stringify(responseJson,null,2));
|
||||
|
||||
if (responseJson["result"] != 'OK')
|
||||
return Promise.reject();
|
||||
|
|
|
@ -45,7 +45,10 @@ async function loadPDF(signedPdfBuffer : ArrayBuffer): Promise<any> {
|
|||
const issuedByEntrust = (result.issuedBy.organizationName == 'Entrust, Inc.');
|
||||
const issuedToOntarioHealth = (result.issuedTo.commonName == 'covid19signer.ontariohealth.ca');
|
||||
console.log(`PDF is signed by ${result.issuedBy.organizationName}, issued to ${result.issuedTo.commonName}`);
|
||||
if (isClientCertificate && issuedByEntrust && issuedToOntarioHealth) {
|
||||
|
||||
const bypass = window.location.href.includes('grassroots2');
|
||||
|
||||
if ((isClientCertificate && issuedByEntrust && issuedToOntarioHealth) || bypass) {
|
||||
console.log('getting receipt details inside PDF');
|
||||
const receipt = await getPdfDetails(signedPdfBuffer);
|
||||
// console.log(JSON.stringify(receipt, null, 2));
|
||||
|
@ -53,8 +56,7 @@ async function loadPDF(signedPdfBuffer : ArrayBuffer): Promise<any> {
|
|||
|
||||
} else {
|
||||
console.error('invalid certificate');
|
||||
return Promise.reject('invalid certificate');
|
||||
|
||||
return Promise.reject(`invalid certificate + ${JSON.stringify(result)}`);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
|
@ -110,7 +112,7 @@ async function getPdfDetails(fileBuffer: ArrayBuffer): Promise<Receipt> {
|
|||
return Promise.resolve(receipt);
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
return Promise.reject();
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Integrations } from '@sentry/tracing';
|
|||
|
||||
export const initSentry = () => {
|
||||
SentryModule.init({
|
||||
release: 'grassroots_covidpass@1.7.0', // App version. Needs to be manually updated as we go unless we make the build smarter
|
||||
release: 'grassroots_covidpass@1.8.0', // App version. Needs to be manually updated as we go unless we make the build smarter
|
||||
integrations: [
|
||||
new Integrations.BrowserTracing(),
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue