NAV
Go Java

Installation

You can clone the repository by using this code

git clone "https://github.com/ito-org/api-backend"

To start with this application, download the current version by using git clone.

The recommended way to install the API server is to use docker with docker-compose.

Docker Compose

To start docker-compose you need to set the following environment variables so that the server knows how to connect to your Postgres database. For that you can add an .env file to the root of the api-backend folder with the following lines.

Environment Variables

Example .env file

POSTGRES_HOST=<Database host>
POSTGRES_DB=<Database name>
POSTGRES_USER=<Username for db>
POSTGRES_PASSWORD=<secretpw>
ParameterRequiredDescription
POSTGRES_HOSTyesThe host of the Postgres database
POSTGRES_DByesThe name of the Postgres database
POSTGRES_USERyesA Postgres user with access to the database
POSTGRES_PASSWORDyesThe database user's secret password

Configuration

Example docker-compose.yaml

version: "3.7"

services:
  go-backend:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8080:8080
    environment:
      - POSTGRES_PASSWORD
      - POSTGRES_USER
      - POSTGRES_DB
    depends_on:
      - postgres
    networks:
      - itonet
    
  postgres:
    image: postgres:12
    environment:
      - POSTGRES_PASSWORD
      - POSTGRES_USER
      - POSTGRES_DB
    ports:
      - 5432:5432
    volumes:
      - ./db/db.sql:/docker-entrypoint-initdb.d/db.sql
      - dbvol:/var/lib/postgresql/data
    networks:
      - itonet
    restart: always
  
networks:
  itonet:

volumes:
  dbvol:

You can copy the docker-compose.yml.example file in the repository to docker-compose.yml to configure a fully working backend with the default configuration.

Run docker-compose up to start the containers in foreground mode or use docker-compose up -d to start them as daemons (in detached mode).

In the default configuration, the HTTP server will be accessible on port 8080.

API

The ito API has intentionally been kept lean, so there is just one endpoint: https://tcn.ito-app.org/tcnreport

This way our code stays easy to understand and to audit.

Initialization of a basic client

package main

import (
	"bytes"
	"net/http"
	"github.com/ito-org/go-backend/tcn"
)

const baseUrl = "https://tcn.ito-app.org"
public class ItoApi {
	private static final String BASE_URL
		= "https://tcn.ito-app.org";
}

Here's how to setup a basic ito client.

Fetching reports

With a GET request you can download all recent reports of infected users. Those are returned as a bytestream (MIME type application/octet-stream) of TCN reports as per the protocol definition.

Example client code for fetching recent reports

private static final int BASELENGTH = 70;
public static List<byte[]> getReports() {
	List<byte[]> reports = new LinkedList<>();
	HttpURLConnection urlConnection = null;
	try {
		URL url = new URL(BASE_URL + "/tcnreport");
		urlConnection
			= (HttpURLConnection) url.openConnection();
		urlConnection.addRequestProperty(
			"Accept",
			"application/octet-stream"
		);
		InputStream in = urlConnection.getInputStream();
		byte[] base = new byte[BASELENGTH];
		byte[] memo;
		int readBytes;
		while ((readBytes = in.read(base, 0, BASELENGTH))
				== BASELENGTH) {
			int memolength
				= (int) base[BASELENGTH - 1] & 0xFF;
			memo = new byte[memolength];
			if (in.read(memo, 0, memolength)
					< memolength) {
				throw new RuntimeException(
					"Parsing from server failed"
				);
			}
			System.out.println(
				"Downloaded TCN Report: "
				+ encodeHexString(base)
				+ encodeHexString(memo)
			);
			ByteBuffer report = ByteBuffer.allocate(
				BASELENGTH + memolength
			);
			report.put(base);
			report.put(memo);
			reports.add(report.array());
		}
		if (readBytes > 0)
			throw new RuntimeException(
				"Parsing from server failed"
			);
	} catch (MalformedURLException e) {
		throw new RuntimeException(e);
	} catch (IOException e) {
		e.printStackTrace();
	} finally {
		if (urlConnection != null) {
			urlConnection.disconnect();
		}
	}
	return reports;
}
func main() {
	_, rak, report, err := tcn.GenerateReport(0, 1, []byte("symptom data"))
	if err != nil {
		t.Error(err)
		return
	}

	signedReport, err := tcn.GenerateSignedReport(rak, report)
	if err != nil {
		t.Error(err)
		return
	}

	b, err := signedReport.Bytes()
	if err != nil {
		t.Error(err)
		return
	}
	rec, req := http.NewRequest("POST", "/tcnreport", bytes.NewReader(b))
	req, _ := http.NewRequest("GET", "/tcnreport", nil)
}

Publishing a report

With a POST request you can report yourself as infected.

Example client code for submitting a report

public static void publishReport(
	byte[] report
) throws IOException {
	HttpURLConnection urlConnection = null;
	try {
		URL url = new URL(BASE_URL + "/tcnreport");
		urlConnection
			= (HttpURLConnection) url.openConnection();
		urlConnection.setDoOutput(true);
		urlConnection.addRequestProperty(
			"Content-Type",
			"application/octet-stream"
		);
		OutputStream outputStream
			= new BufferedOutputStream(
				urlConnection.getOutputStream()
			  );
		outputStream.write(report);
		outputStream.close();

		InputStream inputStream
			= urlConnection.getInputStream();
		inputStream.read();
		inputStream.close();
	} catch (MalformedURLException e) {
		throw new RuntimeException(e);
	} finally {
		if (urlConnection != null)
			urlConnection.disconnect();
	}
}