diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..fefce96 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,49 @@ +# This is a basic workflow to help you get started with Actions + +name: Publish +on: + push: + branches: + - '**' + tags: + - 'v*.*.*' + pull_request: + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + # list of Docker images to use as base name for tags + images: ghcr.io/covidpass-org/covidpass + # generate Docker tags based on the following events/attributes + tags: | + type=schedule + type=edge,branch=dev + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/components/Alert.js b/components/Alert.js index 439e2bb..a7bfad3 100644 --- a/components/Alert.js +++ b/components/Alert.js @@ -9,9 +9,9 @@ export function Alert() { } return( - diff --git a/pages/api/config.js b/pages/api/config.js new file mode 100644 index 0000000..4892943 --- /dev/null +++ b/pages/api/config.js @@ -0,0 +1,4 @@ +export default function handler(req, res) { + // 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 }) +} \ No newline at end of file diff --git a/pages/index.js b/pages/index.js index 5c37529..f5e7db5 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,7 +1,6 @@ import { NextSeo } from 'next-seo'; import Form from '../components/Form' -import Logo from '../components/Logo' import Card from '../components/Card' import Page from '../components/Page' diff --git a/src/constants.js b/src/constants.js index 1974d65..af4c362 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,18 +1,19 @@ -exports.BASE_URL = 'https://raw.githubusercontent.com/ehn-dcc-development/ehn-dcc-valuesets/main/' -exports.API_BASE_URL = 'https://api.covidpass.marvinsextro.de/' +exports.VALUE_SET_BASE_URL = 'https://raw.githubusercontent.com/ehn-dcc-development/ehn-dcc-valuesets/main/' exports.VALUE_TYPES = { - medicalProducts: 'vaccine-medicinal-product.json', - countryCodes: 'country-2-codes.json', - manufacturers: 'vaccine-mah-manf.json', + medicalProducts: 'vaccine-medicinal-product.json', + countryCodes: 'country-2-codes.json', + manufacturers: 'vaccine-mah-manf.json', } exports.COLORS = { - white: 'rgb(255, 255, 255)', - black: 'rgb(0, 0, 0)', - grey: 'rgb(33, 33, 33)', - green: 'rgb(27, 94, 32)', - indigo: 'rgb(26, 35, 126)', - blue: 'rgb(1, 87, 155)', - purple: 'rgb(74, 20, 140)', - teal: 'rgb(0, 77, 64)', + white: 'rgb(255, 255, 255)', + black: 'rgb(0, 0, 0)', + grey: 'rgb(33, 33, 33)', + green: 'rgb(27, 94, 32)', + indigo: 'rgb(26, 35, 126)', + blue: 'rgb(1, 87, 155)', + purple: 'rgb(74, 20, 140)', + teal: 'rgb(0, 77, 64)', } exports.NAME = 'CovidPass' +exports.PASS_IDENTIFIER = 'pass.de.marvinsextro.covidpass' // WELL KNOWN +exports.TEAM_IDENTIFIER = 'X8Q7Q2RLTD' // WELL KNOWN diff --git a/src/pass.js b/src/pass.js index 65860c4..eb18c0c 100644 --- a/src/pass.js +++ b/src/pass.js @@ -1,41 +1,37 @@ 'use strict'; -const consts = require('./constants') +const constants = require('./constants') const utils = require('./utils') -const img = require('./img') const { Payload } = require('./payload') const { toBuffer } = require('do-not-zip') const crypto = require('crypto') exports.createPass = async function(data) { - async function getJSONfromURL(url) { - return await (await fetch(url)).json() - } - function getBufferHash(buffer) { // creating hash const sha = crypto.createHash('sha1'); sha.update(buffer); return sha.digest('hex'); } - + async function signPassWithRemote(pass, payload) { // From pass-js // https://github.com/walletpass/pass-js/blob/2b6475749582ca3ea742a91466303cb0eb01a13a/src/pass.ts - + // Creating new Zip file const zip = [] - + // Adding required files // Create pass.json zip.push({ path: 'pass.json', data: Buffer.from(JSON.stringify(pass)) }) - + const passHash = getBufferHash(Buffer.from(JSON.stringify(pass))) + zip.push({ path: 'icon.png', data: payload.img1x }) zip.push({ path: 'icon@2x.png', data: payload.img2x }) zip.push({ path: 'logo.png', data: payload.img1x }) zip.push({ path: 'logo@2x.png', data: payload.img2x }) - + // adding manifest // Construct manifest here const manifestJson = JSON.stringify( @@ -48,30 +44,35 @@ exports.createPass = async function(data) { ), ); zip.push({ path: 'manifest.json', data: manifestJson }); - - const response = await fetch(consts.API_BASE_URL + 'sign_manifest', { + + // Load API_BASE_URL form nextjs backend + const configResponse = await fetch('/api/config') + const apiBaseUrl = (await configResponse.json()).apiBaseUrl + + const response = await fetch(`${apiBaseUrl}/sign`, { method: 'POST', headers: { 'Accept': 'application/octet-stream', 'Content-Type': 'application/json' }, body: JSON.stringify({ - manifest: manifestJson + PassJsonHash: passHash, + useBlackVersion: !payload.dark }) }) - - if (response.status != 200) { + + if (response.status !== 200) { return undefined } - + const manifestSignature = await response.arrayBuffer() - + zip.push({ path: 'signature', data: Buffer.from(manifestSignature) }); - + // finished! return toBuffer(zip); } - + let valueSets try { @@ -87,8 +88,6 @@ exports.createPass = async function(data) { } catch (e) { return undefined } - - let signingIdentity = await getJSONfromURL(consts.API_BASE_URL + 'signing_identity') const qrCode = { message: payload.raw, @@ -97,14 +96,14 @@ exports.createPass = async function(data) { } const pass = { - passTypeIdentifier: signingIdentity['pass_identifier'], - teamIdentifier: signingIdentity['pass_team_id'], + passTypeIdentifier: constants.PASS_IDENTIFIER, + teamIdentifier: constants.TEAM_IDENTIFIER, sharingProhibited: true, voided: false, formatVersion: 1, - logoText: consts.NAME, - organizationName: consts.NAME, - description: consts.NAME, + logoText: constants.NAME, + organizationName: constants.NAME, + description: constants.NAME, labelColor: payload.labelColor, foregroundColor: payload.foregroundColor, backgroundColor: payload.backgroundColor, @@ -113,75 +112,74 @@ exports.createPass = async function(data) { barcode: qrCode, generic: { headerFields: [ - { - key: "type", - label: "Certificate Type", - value: payload.certificateType + { + key: "type", + label: "Certificate Type", + value: payload.certificateType } ], primaryFields: [ - { - key: "name", - label: "Name", - value: payload.name + { + key: "name", + label: "Name", + value: payload.name } ], secondaryFields: [ - { - key: "dose", - label: "Dose", - value: payload.dose + { + key: "dose", + label: "Dose", + value: payload.dose }, - { - key: "dov", - label: "Date of Vaccination", - value: payload.dateOfVaccination, + { + key: "dov", + label: "Date of Vaccination", + value: payload.dateOfVaccination, textAlignment: "PKTextAlignmentRight" } ], auxiliaryFields: [ - { - key: "vaccine", - label: "Vaccine", - value: payload.vaccineName + { + key: "vaccine", + label: "Vaccine", + value: payload.vaccineName }, - { - key: "dob", - label: "Date of Birth", value: - payload.dateOfBirth, + { + key: "dob", + label: "Date of Birth", value: + payload.dateOfBirth, textAlignment: "PKTextAlignmentRight" } ], backFields: [ - { - key: "uvci", - label: "Unique Certificate Identifier (UVCI)", + { + key: "uvci", + label: "Unique Certificate Identifier (UVCI)", value: payload.uvci }, - { - key: "issuer", - label: "Certificate Issuer", - value: payload.certificateIssuer + { + key: "issuer", + label: "Certificate Issuer", + value: payload.certificateIssuer }, - { - key: "country", - label: "Country of Vaccination", + { + key: "country", + label: "Country of Vaccination", value: payload.countryOfVaccination }, - { - key: "manufacturer", - label: "Manufacturer", - value: payload.manufacturer + { + key: "manufacturer", + label: "Manufacturer", + value: payload.manufacturer }, - { - key: "disclaimer", - label: "Disclaimer", + { + key: "disclaimer", + label: "Disclaimer", value: "This certificate is only valid in combination with the ID card of the certificate holder and expires one year + 14 days after the last dose. The validity of this certificate was not checked by CovidPass." } ] } }; - let buf = await signPassWithRemote(pass, payload) - return buf + return await signPassWithRemote(pass, payload) } diff --git a/src/payload.js b/src/payload.js index dfb78c3..6b1921d 100644 --- a/src/payload.js +++ b/src/payload.js @@ -83,6 +83,7 @@ exports.Payload = class { this.img2x = img2x this.raw = rawData + this.dark = dark this.name = name this.dose = dose diff --git a/src/utils.js b/src/utils.js index 827ed20..a08e30f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,15 +1,15 @@ -const { VALUE_TYPES, BASE_URL } = require("./constants") +const { VALUE_TYPES, VALUE_SET_BASE_URL } = require("./constants") const fetch = require("node-fetch") exports.getValueSets = async function() { - async function getJSONfromURL(url) { + async function getJSONFromURL(url) { return await (await fetch(url)).json() } - var valueSets = {} + let valueSets = {} for (const [key, value] of Object.entries(VALUE_TYPES)) { - valueSets[key] = await getJSONfromURL(BASE_URL + value) + valueSets[key] = await getJSONFromURL(VALUE_SET_BASE_URL + value) } return valueSets