diff --git a/.github/workflows/bank.yml b/.github/workflows/bank.yml index e67cfd13..1ff27cb1 100644 --- a/.github/workflows/bank.yml +++ b/.github/workflows/bank.yml @@ -24,13 +24,14 @@ jobs: # Service container for Postgres services: # Label used to access the service container. - postgres: + bankdb: image: postgres:15.4 env: # Specify the password for Postgres superuser. POSTGRES_PASSWORD: dbos # Set health checks to wait until postgres has started options: >- + --name bankdb --health-cmd pg_isready --health-interval 10s --health-timeout 5s @@ -42,10 +43,12 @@ jobs: steps: - name: "Setup postgres config" run: | - docker exec -i ${{ job.services.postgres.id }} apt update - docker exec -i ${{ job.services.postgres.id }} apt install -y postgresql-15-wal2json - docker exec ${{ job.services.postgres.id }} sh -c 'echo "wal_level=logical" >> /var/lib/postgresql/data/postgresql.conf' - docker restart ${{ job.services.postgres.id }} + docker ps + docker exec bankdb psql -U postgres -c "CREATE USER bank WITH PASSWORD '${PGPASSWORD}';" + docker exec bankdb psql -U postgres -c "ALTER USER bank CREATEDB;" + docker exec bankdb psql -U postgres -c "CREATE DATABASE bank OWNER bank;" + env: + PGPASSWORD: bank - name: Checkout demo app uses: actions/checkout@v3 - name: Use Node.js 18 @@ -54,21 +57,6 @@ jobs: node-version: 18 cache: npm cache-dependency-path: 'bank/bank-backend/package-lock.json' - - - name: Install psql - run: | - sudo apt-get update - sudo apt-get install -y postgresql-client coreutils - - name: Setup Postgres - working-directory: bank/ - run: scripts/init_postgres.sh - env: - # The hostname used to communicate with the PostgreSQL service container - POSTGRES_HOST: localhost - # The default PostgreSQL port - POSTGRES_PORT: 5432 - POSTGRES_PASSWORD: dbos - BANK_PORT: 8081 - name: Compile Bank Server working-directory: bank/bank-backend/ run: | @@ -84,4 +72,4 @@ jobs: # The default PostgreSQL port POSTGRES_PORT: 5432 BANK_PORT: 8081 - NPM_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + BANK_SCHEMA: banktest diff --git a/bank/README.md b/bank/README.md index bd3033de..f4f1118e 100644 --- a/bank/README.md +++ b/bank/README.md @@ -1,10 +1,113 @@ -# Bank of Operon +# Operon Bank Demo App -This is a simple bank application that uses [Operon](https://github.com/dbos-inc/operon) as the backend framework. -It requires node version 18. +This is a simplified bank application that uses [Operon](https://github.com/dbos-inc/operon) as the backend framework. +It requires Node 18.x or later and Docker. -### Start the backend -To compile and run the bank backend, enter the `bank-backend/` directory and follow the instructions [here](bank-backend/README.md). +In this tutorial, you will set up a Postgres `bank` database, start a Keycloak server for authentication, start two backend servers (we will run bank transactions across them), start a frontend server that connects to both backends, and optionally use Jaeger to visualize backend operations. +Especially, bank uses [Prisma](https://www.prisma.io/) to manage the user database. + +## Run the Demo + +### Pre-requisite + +#### Install Dependencies + +This demo includes two packages: the web frontend and the backend. +You need to run `npm install` under each of the [`bank-frontend/`](./bank-frontend/) and [`bank-backend/`](./bank-backend/) directories as a first step. + +#### Start PostgreSQL +Now, let's set up a Postgres database. Please make sure you set `PGPASSWORD` environmental variable before you start! + +We provide a convenient script to start a Postgres server in a docker container: +```shell +./scripts/start_postgres_docker.sh +``` +This script sets up a new user `bank` that owns the database `bank`, and creates a `keycloak` schema in the `bank` database so we can proceed to our next step. + +#### Start Keycloak +[Keycloak](https://www.keycloak.org/) is a popular Identity and Access Management solution. +We use Keycloak to manage user authentication for our bank application. +We provide a script to start a Keycloak server in a docker container: +```shell +./scripts/start_keycloak_docker.sh +``` +You can visit `http://localhost:8083/` after the Keycloak server is started. +We import a default [dbos-realm](./scripts/dbos-realm.json) with the admin name and password (you can use it to log in Keycloak's admin console): +``` +dbos-admin / dbos-pass +``` + +For more information about Keycloak, please visit [Keycloak guides](https://www.keycloak.org/guides#server). + +### Start two backend servers +Each backend server must run in its own terminal window because we will configure different environmental variables for them. +Especially, you need to set `PGPASSWORD` to the one you used to create the `bank` database, and set `BANK_SCHEMA` to the namespace you wish to use for the specific bank server. +In this tutorial, let's set `export BANK_SCHEMA=bank1` for the first server, and in a separate terminal, set `export BANK_SCHEMA=bank2` for the second server. + +In the terminal with `BANK_SCHEMA=bank1`, enter the `bank-backend/` directory, set up the database with Prisma, compile, and start the server at port `8081`: +```bash +# Create tables under the bank1 schema. +npx prisma migrate dev --name initbank1 + +npm run build +npx operon start -p 8081 +``` + +In the terminal with `BANK_SCHEMA=bank2`, enter the `bank-backend/` directory, set up the database with Prisma, and start the server at port `8082`: +```bash +# Create tables under the bank2 schema. +npx prisma migrate dev --name initbank2 + +npx operon start -p 8082 +``` + +Now both backend servers are up, let's move to the bank frontend! ### Start the frontend -We build a bank frontend using Angular. To start the frontend, enter the `bank-frontend/` directory and follow the instructions [here](bank-frontend/README.md). \ No newline at end of file + +We build a simple bank frontend using [Angular](https://angular.io/). To start the frontend, enter the `bank-frontend/` directory and run: +```bash +npm start +``` + +## Demo Walkthrough + +Once you finish all previous steps, navitage to http://localhost:8089/ +You will be presented with a welcome page. +Press the `Login` button and the webpage should redirect to a login page from Keycloak. + +We pre-populate two example users so you can use the following emails and passwords to login: +``` +john@test.com / 123 # This has an "appUser" role. +mike@other.com / pass # This has an "appAdmin" role +``` + +Once you successfully log in, the frontend should re-direct you to the home page of the bank user. +The drop-down menu at the top allows you switch between two bank servers (bank1 at port 8081 and bank2 at port 8082) we just started. +There are three buttons in the middle: +- "New Greeting Message" fetches a greeting message from the backend and displays it in the "Message from Bank" banner above. +- "Create a New Account" creates a new checking account for the current user. If you logged in as `john@test.com`, pressing this button would fail because this user lacks the "appAdmin" permission to create a new account. +- "Refresh Accounts" refreshes the list of accounts of the current user. + +Now, log in as `mike@other.com`. +Once you click "Create a New Account" several times in both bank1 and bank2, you will see a list of accounts displayed with their `Account ID`, `Balance`, `Type`, and `Actions`. Initially, all accounts have zero balance. +Select the "Choose an Action" drop-down menu next to each account, you will see several options: +- "Transaction History" displays a list of past transactions from latest to oldest. +- "Deposit" allows you to make a deposit from either cash or from an account in another bank backend. +- "Withdraw" allows you to make a withdrawal to either cash or to an account in another bank backend. +- "Internal Transfer" allows you to transfer between your own accounts within the same bank backend. + +Sometimes the JWT token would expire and cause failures. You can refresh the page and try again. Refreshing the webpage obtains a new token. + +### (Optional) Visualize Tracing +We use [Jaeger Tracing](https://www.jaegertracing.io/) to visualize traces of Operon operations. We provide a script to automatically start it in a docker container: +```bash +./scripts/start_jaeger_docker.sh +``` +Once it starts, you will see traces via the Jaeger UI: http://localhost:16686/ + +## Under the Covers + +> Note, this section assumes you have read at least the [Operon Getting Started docs](https://docs.dbos.dev/category/getting-started). + +(Coming soon!) \ No newline at end of file diff --git a/bank/bank-backend/.npmrc b/bank/bank-backend/.npmrc deleted file mode 100644 index b11648e4..00000000 --- a/bank/bank-backend/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@dbos-inc:registry=https://npm.pkg.github.com -//npm.pkg.github.com/:_authToken=${NPM_AUTH_TOKEN} diff --git a/bank/bank-backend/README.md b/bank/bank-backend/README.md deleted file mode 100644 index 22654b32..00000000 --- a/bank/bank-backend/README.md +++ /dev/null @@ -1,59 +0,0 @@ -## Bank of Operon -- Backend - -The goal of this app is to show how we use Operon to construct transactions and workflows of a simple banking app. -This app also demonstrates JWT-based authentication and Jaeger tracing with Operon. - -### Initialize Postgres database -You'll need to start a Postgres server, either through Docker, running locally, or using RDS. We provide a script to start a Postgres docker: -```shell -../scripts/start_postgres_docker.sh -``` - -Then, you can set up the database using: -```shell -../scripts/init_postgres.sh -``` - -### Start a Keycloak authentication server -We use Keycloak to perform authentication and we provide a script to automatically start it in a docker container. -You can simply run: -```shell -../scripts/start_keycloak_docker.sh -``` - -### Start a Jaeger tracing server -We use Jaeger to visualize traces of Operon operations. We provide a script to automatically start it in a docker container: -```shell -../scripts/start_jaeger_docker.sh -``` - -You can see traces via the Jaeger UI: http://localhost:16686/ - -### Start the backend using Operon -First, install dependencies. -```shell -npm ci -``` - -If you just created a database, you can configure your database path in the `.env` file and create tables using Prisma: -```shell -# .env sets default database url to: DATABASE_URL="postgresql://bank:bank@localhost:5432/bank?schema=prisma" -npx prisma migrate dev --name init -``` - -Then compile this app: -```shell -npm run build -``` - -You can run tests and make sure everything works properly: -```shell -npm test -``` - -Finally, start the simple HTTP server using Operon CLI: -```shell -npx operon start -``` - -Now by default, the bank backend should start at `http://localhost:8081`! You can now send HTTP requests to its REST endpoints, or use it with the [frontend](../bank-frontend/). \ No newline at end of file diff --git a/bank/bank-backend/operon-config.yaml b/bank/bank-backend/operon-config.yaml index 453fb3d7..bd4a2648 100644 --- a/bank/bank-backend/operon-config.yaml +++ b/bank/bank-backend/operon-config.yaml @@ -18,5 +18,6 @@ telemetry: localRuntimeConfig: port: 8081 application: - bankname: 'localbank' + bankname: "Operon Bank - ${BANK_SCHEMA}" bankport: '8081' + bankschema: ${BANK_SCHEMA} diff --git a/bank/bank-backend/operon-test-config.yaml b/bank/bank-backend/operon-test-config.yaml index c4b35f31..ef7714f8 100644 --- a/bank/bank-backend/operon-test-config.yaml +++ b/bank/bank-backend/operon-test-config.yaml @@ -17,5 +17,6 @@ telemetry: localRuntimeConfig: port: 8081 application: - bankname: 'localbank' - bankport: '8081' + bankname: "testbank - ${BANK_SCHEMA}" + bankport: '8091' + bankschema: ${BANK_SCHEMA} \ No newline at end of file diff --git a/bank/bank-backend/package-lock.json b/bank/bank-backend/package-lock.json index 8166991d..45cd6034 100644 --- a/bank/bank-backend/package-lock.json +++ b/bank/bank-backend/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "ISC", "dependencies": { - "@dbos-inc/operon": "0.5.32-preview", + "@dbos-inc/operon": "0.5.34-preview", "@koa/bodyparser": "^5.0.0", "@koa/cors": "^4.0.0", "@koa/router": "^12.0.0", @@ -530,9 +530,9 @@ } }, "node_modules/@dbos-inc/operon": { - "version": "0.5.32-preview", - "resolved": "https://npm.pkg.github.com/download/@dbos-inc/operon/0.5.32-preview/aa1ff0c71d6d83789e6074a37bf16200e4bc5461", - "integrity": "sha512-o4W+W6S/AcDggZUS4Nd2G/o3LuQzCYShNjeI90sA1HOsmAq3dBEAvSb7EMIRfw1RMuRVBFcYFikf+d6aQa6XJQ==", + "version": "0.5.34-preview", + "resolved": "https://registry.npmjs.org/@dbos-inc/operon/-/operon-0.5.34-preview.tgz", + "integrity": "sha512-Ztg41iDfZWtoOIoMDK0sLE91200YKA7Qmg1+TI31iyIfa5/DD+Ezg000yhZa8YYZ6xfuYV6YluwwRzgVwDFPDA==", "dependencies": { "@koa/bodyparser": "^5.0.0", "@koa/cors": "^4.0.0", @@ -2876,9 +2876,9 @@ } }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "engines": { "node": ">=16" } diff --git a/bank/bank-backend/package.json b/bank/bank-backend/package.json index cf6dca27..cee43304 100644 --- a/bank/bank-backend/package.json +++ b/bank/bank-backend/package.json @@ -15,7 +15,7 @@ "license": "ISC", "private": true, "dependencies": { - "@dbos-inc/operon": "0.5.32-preview", + "@dbos-inc/operon": "0.5.34-preview", "@koa/bodyparser": "^5.0.0", "@koa/cors": "^4.0.0", "@koa/router": "^12.0.0", diff --git a/bank/bank-backend/prisma/.env b/bank/bank-backend/prisma/.env index b9203d3e..5b214456 100644 --- a/bank/bank-backend/prisma/.env +++ b/bank/bank-backend/prisma/.env @@ -4,4 +4,4 @@ # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. # See the documentation for all the connection string options: https://pris.ly/d/connection-strings -DATABASE_URL="postgresql://bank:bank@localhost:5432/bank?schema=prisma" +DATABASE_URL=postgresql://bank:${PGPASSWORD}@localhost:5432/bank?schema=${BANK_SCHEMA} diff --git a/bank/bank-backend/src/bank.test.ts b/bank/bank-backend/src/bank.test.ts index 436bb112..a9593e77 100644 --- a/bank/bank-backend/src/bank.test.ts +++ b/bank/bank-backend/src/bank.test.ts @@ -6,10 +6,15 @@ import { convertTransactionHistory } from "./router"; describe("bank-tests", () => { let testRuntime: OperonTestingRuntime; + let bankSchema: string; beforeAll(async () => { + bankSchema = process.env.BANK_SCHEMA ?? ""; + if (!bankSchema) { + throw new Error("Env 'BANK_SCHEMA' not set!"); + } testRuntime = await createTestingRuntime([BankEndpoints, BankAccountInfo, BankTransactionHistory], "operon-test-config.yaml"); - await testRuntime.queryUserDB(`delete from prisma."AccountInfo" where "ownerName"=$1;`, "alice"); + await testRuntime.queryUserDB(`delete from ${bankSchema}."AccountInfo" where "ownerName"=$1;`, "alice"); }); afterAll(async () => { @@ -28,7 +33,7 @@ describe("bank-tests", () => { type: "saving", }); - const res = await testRuntime.queryUserDB(`select * from prisma."AccountInfo" where "ownerName" = $1;`, "alice"); + const res = await testRuntime.queryUserDB(`select * from ${bankSchema}."AccountInfo" where "ownerName" = $1;`, "alice"); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access expect(res[0].ownerName).toBe("alice"); // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access diff --git a/bank/bank-backend/src/router.ts b/bank/bank-backend/src/router.ts index 5f73da1b..80e501d8 100644 --- a/bank/bank-backend/src/router.ts +++ b/bank/bank-backend/src/router.ts @@ -11,7 +11,7 @@ export class BankEndpoints { // eslint-disable-next-line @typescript-eslint/require-await @GetApi("/api/greeting") static async greeting(ctx: HandlerContext) { - return { msg: "Hello from DBOS Operon " + ctx.getConfig("bankname") }; + return { msg: "Hello from " + ctx.getConfig("bankname") }; } // Deposit. diff --git a/bank/bank-backend/src/runtime.test.ts b/bank/bank-backend/src/runtime.test.ts index aa881556..e1054068 100644 --- a/bank/bank-backend/src/runtime.test.ts +++ b/bank/bank-backend/src/runtime.test.ts @@ -40,6 +40,10 @@ async function waitForMessageTest(command: ChildProcess, port: string) { describe("runtime-tests", () => { beforeAll(() => { + const bankSchema = process.env.BANK_SCHEMA; + if (!bankSchema) { + throw new Error("Env 'BANK_SCHEMA' not set!"); + } execSync('npm install'); execSync('npm run build'); }); diff --git a/bank/bank-frontend/README.md b/bank/bank-frontend/README.md deleted file mode 100644 index 7796ebad..00000000 --- a/bank/bank-frontend/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Bank Frontend -First, install dependencies. -```shell -npm ci -``` - -Then, you can compile and serve the frontend. -```shell -npm start -``` - -Now, you should be able to view the bank website in your browser: http://localhost:8089/ diff --git a/bank/bank-frontend/src/environments/environment.ts b/bank/bank-frontend/src/environments/environment.ts index 2232b40f..0baf99ef 100644 --- a/bank/bank-frontend/src/environments/environment.ts +++ b/bank/bank-frontend/src/environments/environment.ts @@ -1,6 +1,6 @@ export const environment = { // Put a list of actual bank servers here. - bankHosts: ['http://localhost:8081', 'http://localhost:8081'], + bankHosts: ['http://localhost:8081', 'http://localhost:8082'], authUrl: `http://localhost:8083/realms/dbos/protocol/openid-connect`, redirectUrl: "http://localhost:8089/" } diff --git a/bank/scripts/init_postgres.sh b/bank/scripts/init_postgres.sh deleted file mode 100755 index 5332b3e6..00000000 --- a/bank/scripts/init_postgres.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# To run this script without manually typing password, you need to set POSTGRES_PASSWORD env to the superuser password. -# Get the user name and password from env. -if [[ -z "${BANK_DB_NAME}" ]]; then - export BANK_DB_NAME="bank" -fi - -if [[ -z "${BANK_DB_PASSWORD}" ]]; then - export BANK_DB_PASSWORD="bank" -fi - -if [[ -z "${POSTGRES_HOST}" ]]; then - export POSTGRES_HOST="localhost" -fi - -if [[ -z "${POSTGRES_PORT}" ]]; then - export POSTGRES_PORT=5432 -fi - -export PGPASSWORD="${POSTGRES_PASSWORD}" - -# Create a new user and the database with the same name. -psql -U postgres -h ${POSTGRES_HOST} -p ${POSTGRES_PORT} -c "CREATE USER ${BANK_DB_NAME} WITH PASSWORD '${BANK_DB_PASSWORD}';" -psql -U postgres -h $POSTGRES_HOST -p ${POSTGRES_PORT} -c "ALTER USER ${BANK_DB_NAME} CREATEDB;" -export PGPASSWORD="${BANK_DB_PASSWORD}" - -# Drop if exists and create a new one. -psql -U ${BANK_DB_NAME} -h ${POSTGRES_HOST} -d postgres -p ${POSTGRES_PORT} -c "DROP DATABASE IF EXISTS ${BANK_DB_NAME};" -psql -U ${BANK_DB_NAME} -h ${POSTGRES_HOST} -d postgres -p ${POSTGRES_PORT} -c "CREATE DATABASE ${BANK_DB_NAME} OWNER ${BANK_DB_NAME};" - -echo "Database user and DB created." - -# Create Keycloak schema -psql -U ${BANK_DB_NAME} -h ${POSTGRES_HOST} -d ${BANK_DB_NAME} -p ${POSTGRES_PORT} -c "CREATE SCHEMA IF NOT EXISTS keycloak;" \ No newline at end of file diff --git a/bank/scripts/start_keycloak_docker.sh b/bank/scripts/start_keycloak_docker.sh index 80419d74..d8d7a0ea 100755 --- a/bank/scripts/start_keycloak_docker.sh +++ b/bank/scripts/start_keycloak_docker.sh @@ -2,27 +2,21 @@ SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" -if [[ -z "${BANK_DB_NAME}" ]]; then - export BANK_DB_NAME="bank" +# Check if PGPASSWORD is set +if [[ -z "${PGPASSWORD}" ]]; then + echo "Error: PGPASSWORD is not set." >&2 + exit 1 fi -if [[ -z "${BANK_DB_PASSWORD}" ]]; then - export BANK_DB_PASSWORD="bank" -fi - -if [[ -z "${POSTGRES_HOST}" ]]; then - export POSTGRES_HOST="host.docker.internal" -fi - -if [[ -z "${POSTGRES_PORT}" ]]; then - export POSTGRES_PORT=5432 -fi +BANK_DB_NAME="bank" +POSTGRES_HOST="host.docker.internal" +POSTGRES_PORT=5432 docker pull quay.io/keycloak/keycloak:latest -# This script will start a Keycloak server in a docker container with some pre-configured users in the DBOS realm. -docker run -d --rm -p 8083:8080 --name keycloak -e KEYCLOAK_ADMIN=dbos-admin -e KEYCLOAK_ADMIN_PASSWORD=dbos-pass \ +# This script will start a Keycloak server in a docker container with two pre-configured users in the DBOS realm. +docker run --rm -d -p 8083:8080 --name keycloak -e KEYCLOAK_ADMIN=dbos-admin -e KEYCLOAK_ADMIN_PASSWORD=dbos-pass \ -e KC_DB=postgres -e KC_DB_DATABASE=${BANK_DB_NAME} -e KC_DB_SCHEMA=keycloak \ - -e KC_DB_URL_DATABASE=${BANK_DB_NAME} -e KC_DB_PASSWORD=${BANK_DB_PASSWORD} -e KC_DB_URL_HOST=${POSTGRES_HOST} -e KC_DB_URL_PORT=${POSTGRES_PORT} -e KC_DB_USERNAME=${BANK_DB_NAME} \ + -e KC_DB_URL_DATABASE=${BANK_DB_NAME} -e KC_DB_PASSWORD=${PGPASSWORD} -e KC_DB_URL_HOST=${POSTGRES_HOST} -e KC_DB_URL_PORT=${POSTGRES_PORT} -e KC_DB_USERNAME=${BANK_DB_NAME} \ -v ${SCRIPT_DIR}/dbos-realm.json:/opt/keycloak/data/import/realm.json \ quay.io/keycloak/keycloak:latest start-dev --import-realm diff --git a/bank/scripts/start_postgres_docker.sh b/bank/scripts/start_postgres_docker.sh index 3c8195f0..27c1b817 100755 --- a/bank/scripts/start_postgres_docker.sh +++ b/bank/scripts/start_postgres_docker.sh @@ -1,4 +1,33 @@ #!/bin/bash -# Start a local Posgres docker -docker run --name=bankdb --env=POSTGRES_PASSWORD=dbos --env=PGDATA=/var/lib/postgresql/data --volume=/var/lib/postgresql/data -p 5432:5432 -d postgres:latest \ No newline at end of file +# Check if PGPASSWORD is set +if [[ -z "${PGPASSWORD}" ]]; then + echo "Error: PGPASSWORD is not set." >&2 + exit 1 +fi + +# Start Postgres in a local Docker container +docker run --rm --name=bankdb --env=POSTGRES_PASSWORD=${PGPASSWORD} --env=PGDATA=/var/lib/postgresql/data --volume=/var/lib/postgresql/data -p 5432:5432 -d postgres:15.4 + +# Wait for PostgreSQL to start +echo "Waiting for PostgreSQL to start..." +for i in {1..30}; do + if docker exec bankdb pg_isready -U postgres > /dev/null; then + echo "PostgreSQL started!" + break + fi + sleep 1 +done + +# Create a new user and the database with the same name ('bank'). +docker exec bankdb psql -U postgres -c "CREATE USER bank WITH PASSWORD '${PGPASSWORD}';" +docker exec bankdb psql -U postgres -c "ALTER USER bank CREATEDB;" + +# Drop if exists and create a new one. +docker exec bankdb psql -U postgres -c "CREATE DATABASE bank OWNER bank;" + +echo "Database user: 'bank' and database: 'bank' created." + +# Create Keycloak schema +docker exec bankdb psql -U bank -d bank -c "CREATE SCHEMA IF NOT EXISTS keycloak;" +echo "Schema: 'keycloak' created." \ No newline at end of file