6 Commits

Author SHA1 Message Date
rlkollman 2af2b28d84 Updated zestimate to use puppeteer 2026-05-24 08:57:32 -05:00
rlkollman 76c6eefb36 Add install.sh 2026-05-24 08:55:14 -05:00
Robert Dyer 2b355bf057 pin chromium versions 2025-05-29 13:16:15 -05:00
Robert Dyer 6759916323 more specific base version 2025-05-29 13:13:26 -05:00
google-labs-jules[bot] 6124a6d582 feat: Use Node.js Alpine base image in Dockerfile
Updates the Dockerfile to use `node:22-alpine` as the base image,
reducing the overall image size and standardizing on Alpine Linux.

Key changes include:
- Replaced `apt-get` with `apk add --no-cache` for package installation.
- Updated package names to their Alpine equivalents (e.g., `libasound2` to `alsa-lib`).
- Switched from manual Chrome/Chromedriver downloads to installing `chromium`
  and `chromium-chromedriver` from Alpine repositories.
- Pinned `chromium` and `chromium-chromedriver` to version `136.0.7103.113-r0`
  for build consistency.
- Ensured correct ordering for `WORKDIR`, directory creation/permissions,
  and `USER node` commands.
2025-05-29 18:10:09 +00:00
google-labs-jules[bot] 1d50da987f Refactor: Update Dockerfile to Node.js Alpine base
I've switched the base image from node:22 to node:22-alpine to reduce image size.

Key changes include:
- Updated FROM instruction to node:22-alpine.
- Replaced apt-get with apk for package management.
- Updated package names to their Alpine equivalents (e.g., libasound2 to alsa-lib, libgtk-4-1 to gtk+3.0).
- Modified Chrome and Chromedriver installation to use Alpine's `chromium` and `chromium-chromedriver` packages instead of manual downloads.
- Ensured correct user, working directory permissions, and order of operations, particularly for directory creation and ownership before switching to the 'node' user.

Note: The Docker image build process encountered an environmental 'no space left on device' error. Therefore, I could not complete full build verification and subsequent functional testing of the image. The Dockerfile changes are based on best practices for Alpine conversion.
2025-05-22 05:07:00 +00:00
3 changed files with 109 additions and 44 deletions
+17 -22
View File
@@ -1,32 +1,25 @@
# Use an official Node.js runtime as a parent image
FROM node:22
FROM node:22.16.0-alpine3.21
RUN apt-get update -qq -y && \
apt-get install -y \
libasound2 \
libatk-bridge2.0-0 \
libgtk-4-1 \
libnss3 \
RUN apk add --no-cache \
alsa-lib \
at-spi2-atk \
gtk+3.0 \
nss \
xdg-utils \
wget && \
wget -q -O chrome-linux64.zip https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.204/linux64/chrome-linux64.zip && \
unzip chrome-linux64.zip && \
rm chrome-linux64.zip && \
mv chrome-linux64 /opt/chrome/ && \
ln -s /opt/chrome/chrome /usr/local/bin/ && \
wget -q -O chromedriver-linux64.zip https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.204/linux64/chromedriver-linux64.zip && \
unzip -j chromedriver-linux64.zip chromedriver-linux64/chromedriver && \
rm chromedriver-linux64.zip && \
mv chromedriver /usr/local/bin/
# Don't run as root
USER node
wget \
unzip \
chromium=136.0.7103.113-r0 \
chromium-chromedriver=136.0.7103.113-r0
# Set the working directory in the container
WORKDIR /usr/src/app
# Create the cache directory
RUN mkdir -p ./cache && chown node:node ./cache
# Create the cache directory and set ownership (as root)
RUN mkdir -p ./cache && chown -R node:node ./cache
# Don't run as root
USER node
# Define environment variables
ENV NODE_ENV=production
@@ -67,9 +60,11 @@ ENV BITCOIN_PAYEE_NAME="Bitcoin Price Change"
VOLUME ./cache
# Copy the current directory contents into the container at /usr/src/app
# This should happen after WORKDIR is set and USER is node
COPY --chown=node:node . .
# Install any needed packages specified in package.json
# This should run as the node user
RUN npm install && npm update
# Run the app when the container launches
+50
View File
@@ -0,0 +1,50 @@
# Require APT to utilizelocal proxy
echo "Acquire::http::proxy \"http://192.168.128.185:3142\";" >> /etc/apt/apt.conf.d/02proxy
# Set TimeZone :)
timedatectl set-timezone America/Chicago
# Perform update / upgrade of all existing packages and repos
apt update && apt upgrade -y
# Install all identified dependencies
apt install curl git ca-certificates fonts-liberation libasound2 libatk-bridge2.0-0 libatk1.0-0 \
libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 \
libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 \
libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils -y
# Add NodeSource repository (replace 22.x with your desired version, e.g., 20.x, 18.x)
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
# Install Node.js
apt install -y nodejs
# Git clone the actual-helpers repo
cd /opt
git clone https://git.kollman.net/rlkollman/actual-helpers
# Install all dependencies
cd /opt/actual-helpers
npm install
# Write crontab file with scheduled tasks
echo "" >> /etc/crontab
echo "# Sync ActualBudget Bank Accounts daily at 2 AM" >> /etc/crontab
echo "00 2 * * * root cd /opt/actual-helpers && node sync-banks.js > /dev/null" >> /etc/crontab
echo "05 2 * * * root cd /opt/actual-helpers && node update-investments.js > /dev/null" >> /etc/crontab
echo "" >> /etc/crontab
echo "# Sync ActualBudget Vehicles & Apply Mortgage Interest on the 1st day of every month at 3 AM" >> /etc/crontab
echo "00 3 1 * * root cd /opt/actual-helpers && node kbb.js > /dev/null" >> /etc/crontab
echo "15 3 6 * * root cd /opt/actual-helpers && node apply-interest.js > /dev/null" >> /etc/crontab
echo "" >> /etc/crontab
echo "# Sync ActualBudget Residence through RentCast on the 1st & 15th day of every month at 4 AM" >> /etc/crontab
echo "#00 4 1 * * root cd /opt/actual-helpers && node rentcast.js > /dev/null" >> /etc/crontab
echo "#00 4 15 * * root cd /opt/actual-helpers && node rentcast.js > /dev/null" >> /etc/crontab
echo "" >> /etc/crontab
echo "# Sync ActualBudget Residence through Zillow on the 1st & 16th day of every month at 5 AM" >> /etc/crontab
echo "30 4 1 * * root cd /opt/actual-helpers && node zestimate.js > /dev/null" >> /etc/crontab
echo "30 4 16 * * root cd /opt/actual-helpers && node zestimate.js > /dev/null" >> /etc/crontab
echo "" >> /etc/crontab
echo "# Output the current budget status to a file" >> /etc/crontab
echo "00 5 * * * root cd /opt/actual-helpers && node budget.js > /root/budget.txt" >> /etc/crontab
+42 -22
View File
@@ -1,35 +1,55 @@
const { Builder, Browser, By, until } = require('selenium-webdriver')
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const api = require('@actual-app/api');
const { closeBudget, ensurePayee, getAccountBalance, getAccountNote, getTagValue, openBudget, showPercent, sleep } = require('./utils');
require("dotenv").config();
puppeteer.use(StealthPlugin());
async function getZestimate(URL) {
let driver = await new Builder()
.forBrowser(Browser.CHROME)
.build();
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
try {
await driver.get(URL);
const html = await driver.wait(until.elementLocated(By.css('body')), 5000).getAttribute('innerHTML');
try {
let match = html.match(/"zestimate":"(\d+)"/);
if (match) {
return parseInt(match[1]) * 100;
await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 20000 });
const html = await page.content();
try {
console.log('Got html data!');
let match = html.match(/"price":"(\d+)"/);
if (match) {
console.log('matched 1st');
return parseInt(match[1]) * 100;
}
match = html.match(/\\"price\\":(\d+)/);
if (match) {
console.log('matched 2nd');
return parseInt(match[1]) * 100;
}
match = html.match(/\\"price\\":\\"(\d+)\\"/);
if (match) {
console.log('matched 3rd');
return parseInt(match[1]) * 100;
}
console.log('didn\'t match any :(');
console.log('~~!!');
// console.log(html);
console.log('!!~~');
} catch (error) {
console.log('Error parsing Zillow page:');
console.log(error);
console.log(html);
}
match = html.match(/\\"zestimate\\":\\"(\d+)\\"/);
if (match) {
return parseInt(match[1]) * 100;
}
} catch (error) {
console.log('Error parsing Zillow page:');
console.log(error);
console.log(html);
}
} catch (er) {
console.log('Error while fetching zestimate:');
console.log(er);
console.lo(html);
} finally {
await driver.quit();
await browser.close();
}
return undefined;
}