blob: dc3c6301a65f3b5e3bb79450b79220d37055c15d [file]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
FINERACT_PID_FILE="${RUNTIME_DIR}/fineract-devrun.pid"
FINERACT_LOG_FILE="${RUNTIME_DIR}/fineract-devrun.log"
start_fineract_backend() {
if check_https_health "${FINERACT_HEALTH_URL}"; then
echo " ✅ Fineract backend is already running"
return
fi
if [[ -f "${FINERACT_PID_FILE}" ]]; then
local existing_pid
existing_pid="$(cat "${FINERACT_PID_FILE}")"
if kill -0 "${existing_pid}" >/dev/null 2>&1; then
echo " ⏳ Waiting for existing Fineract process (${existing_pid}) to be healthy..."
wait_for_https_health "${FINERACT_HEALTH_URL}" 120
echo " ✅ Fineract backend is running"
return
fi
rm -f "${FINERACT_PID_FILE}"
fi
echo " 🚀 Starting Fineract backend via PowerShell..."
local repo_win log_win pid_win java_home_win
repo_win="$(to_windows_path "${FINERACT_REPO_PATH}")"
log_win="$(to_windows_path "${FINERACT_LOG_FILE}")"
pid_win="$(to_windows_path "${FINERACT_PID_FILE}")"
java_home_win="C:\\Program Files\\Java\\jdk-21"
local ps1_file
ps1_file="${RUNTIME_DIR}/start_fineract.ps1"
cat > "${ps1_file}" <<POWERSHELL
Set-Location -LiteralPath '${repo_win}';
\$env:JAVA_HOME='${java_home_win}';
\$env:Path="\$env:JAVA_HOME\bin;\$env:Path";
\$env:JAVA_TOOL_OPTIONS='-Duser.timezone=UTC';
\$env:FINERACT_HIKARI_DRIVER_SOURCE_CLASS_NAME='org.postgresql.Driver';
\$env:FINERACT_HIKARI_JDBC_URL='jdbc:postgresql://localhost:${SOURCE_DB_HOST_PORT}/${SOURCE_TENANTS_DB_NAME}';
\$env:FINERACT_HIKARI_USERNAME='${SOURCE_APP_USER}';
\$env:FINERACT_HIKARI_PASSWORD='${SOURCE_APP_PASSWORD}';
\$env:FINERACT_DEFAULT_TENANTDB_HOSTNAME='localhost';
\$env:FINERACT_DEFAULT_TENANTDB_PORT='${SOURCE_DB_HOST_PORT}';
\$env:FINERACT_DEFAULT_TENANTDB_UID='${SOURCE_APP_USER}';
\$env:FINERACT_DEFAULT_TENANTDB_PWD='${SOURCE_APP_PASSWORD}';
\$env:FINERACT_DEFAULT_TENANTDB_NAME='${SOURCE_DB_NAME}';
\$env:FINERACT_DEFAULT_TENANTDB_IDENTIFIER='${FINERACT_TENANT_ID}';
\$env:FINERACT_DEFAULT_TENANTDB_TIMEZONE='Asia/Kolkata';
\$proc = Start-Process -FilePath 'cmd.exe' -WorkingDirectory '${repo_win}' -ArgumentList '/c gradlew.bat --no-daemon :fineract-provider:devRun > "${log_win}" 2>&1' -PassThru -WindowStyle Hidden;
Set-Content -LiteralPath '${pid_win}' -Value \$proc.Id;
Write-Host "Started Fineract with PID: \$proc.Id";
POWERSHELL
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$(to_windows_path "${ps1_file}")"
echo " ⏳ Waiting for Fineract backend to become healthy (up to 2 minutes)..."
wait_for_https_health "${FINERACT_HEALTH_URL}" 120
echo " ✅ Fineract backend is running"
}
enable_business_date() {
docker compose -f "${COMPOSE_FILE}" exec -T fineract-db \
env PGPASSWORD="${SOURCE_BOOTSTRAP_PASSWORD}" \
psql -h localhost -U "${SOURCE_BOOTSTRAP_USER}" -d "${SOURCE_DB_NAME}" \
-c "UPDATE c_configuration SET enabled = TRUE WHERE name = 'enable-business-date';" >/dev/null
}
create_source_compatibility_views() {
local sql_file="${RUNTIME_DIR}/create_views.sql"
cat > "${sql_file}" <<EOF
CREATE SCHEMA IF NOT EXISTS ${SOURCE_DB_SCHEMA};
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_office AS
SELECT id, parent_id, hierarchy, external_id, name, opening_date,
'1970-01-01 00:00:00+00'::timestamptz AS created_on_utc,
'1970-01-01 00:00:00+00'::timestamptz AS last_modified_on_utc
FROM public.m_office;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_currency AS
SELECT id, code, decimal_places, currency_multiplesof, display_symbol, name, internationalized_name_code,
'1970-01-01 00:00:00+00'::timestamptz AS created_on_utc,
'1970-01-01 00:00:00+00'::timestamptz AS last_modified_on_utc
FROM public.m_currency;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_client AS
SELECT id, account_no, external_id, status_enum, activation_date, office_joining_date, office_id, staff_id, gender_cv_id, date_of_birth, legal_form_enum, submittedon_date, updated_on, created_on_utc, last_modified_on_utc
FROM public.m_client;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_product_loan AS
SELECT id, short_name, currency_code, currency_digits, currency_multiplesof, principal_amount, min_principal_amount, max_principal_amount, arrearstolerance_amount, name, description, nominal_interest_rate_per_period, annual_nominal_interest_rate, repay_every, repayment_period_frequency_enum, number_of_repayments, overdue_days_for_npa, start_date, close_date,
'1970-01-01 00:00:00+00'::timestamptz AS created_on_utc,
'1970-01-01 00:00:00+00'::timestamptz AS last_modified_on_utc
FROM public.m_product_loan;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_loan AS
SELECT id, account_no, external_id, client_id, product_id, loan_status_id, loan_type_enum, currency_code, currency_digits, currency_multiplesof, principal_amount_proposed, principal_amount, approved_principal, net_disbursal_amount, annual_nominal_interest_rate, nominal_interest_rate_per_period, interest_method_enum, interest_calculated_in_period_enum, term_frequency, term_period_frequency_enum, repay_every, repayment_period_frequency_enum, number_of_repayments, amortization_method_enum, submittedon_date, approvedon_date, expected_disbursedon_date, expected_firstrepaymenton_date, disbursedon_date, expected_maturedon_date, maturedon_date, principal_disbursed_derived, principal_repaid_derived, principal_writtenoff_derived, principal_outstanding_derived, interest_charged_derived, interest_repaid_derived, interest_writtenoff_derived, interest_outstanding_derived, fee_charges_outstanding_derived, penalty_charges_outstanding_derived, total_expected_repayment_derived, total_repayment_derived, total_writtenoff_derived, total_outstanding_derived, loan_counter, is_npa, created_on_utc, last_modified_on_utc
FROM public.m_loan;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_loan_transaction AS
SELECT id, loan_id, office_id, is_reversed, transaction_type_enum, transaction_date, amount, principal_portion_derived, interest_portion_derived, fee_charges_portion_derived, penalty_charges_portion_derived, outstanding_loan_balance_derived, submitted_on_date, created_on_utc, last_modified_on_utc
FROM public.m_loan_transaction;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_delinquency_range AS
SELECT id, classification, min_age_days, max_age_days, created_on_utc, last_modified_on_utc
FROM public.m_delinquency_range;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_delinquency_bucket AS
SELECT id, name, created_on_utc, last_modified_on_utc
FROM public.m_delinquency_bucket;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_delinquency_bucket_mappings AS
SELECT id, delinquency_range_id, delinquency_bucket_id, created_on_utc, last_modified_on_utc
FROM public.m_delinquency_bucket_mappings;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_loan_delinquency_tag_history AS
SELECT id, delinquency_range_id, loan_id, addedon_date, liftedon_date, created_on_utc, last_modified_on_utc
FROM public.m_loan_delinquency_tag_history;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.batch_job_execution AS
SELECT job_execution_id, status, start_time AT TIME ZONE 'UTC' AS start_time, end_time AT TIME ZONE 'UTC' AS end_time, exit_code, exit_message, create_time AT TIME ZONE 'UTC' AS created_on_utc, last_updated AT TIME ZONE 'UTC' AS last_modified_on_utc
FROM public.batch_job_execution;
EOF
docker compose -f "${COMPOSE_FILE}" cp "${sql_file}" fineract-db:/tmp/create_views.sql
docker compose -f "${COMPOSE_FILE}" exec -T fineract-db \
env PGPASSWORD="${SOURCE_BOOTSTRAP_PASSWORD}" \
psql -h localhost -U "${SOURCE_BOOTSTRAP_USER}" -d "${SOURCE_DB_NAME}" -f /tmp/create_views.sql >/dev/null
}
grant_reader_access() {
local sql_file="${RUNTIME_DIR}/grant_reader.sql"
cat > "${sql_file}" <<EOF
GRANT USAGE ON SCHEMA public TO ${SOURCE_REPLICA_USER};
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${SOURCE_REPLICA_USER};
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO ${SOURCE_REPLICA_USER};
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO ${SOURCE_REPLICA_USER};
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO ${SOURCE_REPLICA_USER};
GRANT USAGE ON SCHEMA ${SOURCE_DB_SCHEMA} TO ${SOURCE_REPLICA_USER};
GRANT SELECT ON ALL TABLES IN SCHEMA ${SOURCE_DB_SCHEMA} TO ${SOURCE_REPLICA_USER};
EOF
docker compose -f "${COMPOSE_FILE}" cp "${sql_file}" fineract-db:/tmp/grant_reader.sql
docker compose -f "${COMPOSE_FILE}" exec -T fineract-db \
env PGPASSWORD="${SOURCE_BOOTSTRAP_PASSWORD}" \
psql -h localhost -U "${SOURCE_BOOTSTRAP_USER}" -d "${SOURCE_DB_NAME}" -f /tmp/grant_reader.sql >/dev/null
}
ensure_docker_prerequisites
load_environment
ensure_fineract_repo
require_command curl
echo "=== Bootstrapping Fineract Source ==="
echo ""
echo "Waiting for fineract-db to be healthy..."
wait_for_compose_service fineract-db 60 pg_isready -U "${SOURCE_BOOTSTRAP_USER}" -d postgres
echo " ✅ fineract-db is healthy"
echo ""
echo "Starting Fineract backend..."
start_fineract_backend
echo ""
echo "Enabling business date..."
enable_business_date
echo " ✅ Business date enabled"
echo ""
echo "Creating source compatibility views..."
create_source_compatibility_views
echo " ✅ Compatibility views created"
echo ""
echo "Granting reader access..."
grant_reader_access
echo " ✅ Reader access granted"
echo ""
echo "=== Fineract Source Bootstrap Complete ==="