chore: rename to ReceiptNext + deployment scripts
- Full rename: module path, imports, directory structure - install.sh: one-command bare-metal deployment - contrib/receiptnext.service: systemd service file - Makefile: build, install, manage service - .gitenv updated to ReceiptNext - New Releases page on git.lohmar.co.uk/cclohmar/ReceiptNext
This commit is contained in:
parent
5ca3ff7555
commit
f50048e833
9 changed files with 279 additions and 16 deletions
85
Makefile
Normal file
85
Makefile
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# ReceiptNext — AI-Powered Expense Tracker
|
||||
# Makefile for build, install, and deployment
|
||||
|
||||
BINARY = receiptnext
|
||||
OUTDIR = dist
|
||||
VERSION = v1.0.0
|
||||
LDFLAGS = -s -w
|
||||
|
||||
.PHONY: all build clean install uninstall run stop restart logs
|
||||
|
||||
all: build
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Build
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
build:
|
||||
go build -ldflags="$(LDFLAGS)" -o $(BINARY) .
|
||||
|
||||
build-linux-amd64:
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="$(LDFLAGS)" -o $(OUTDIR)/$(BINARY)-linux-amd64 .
|
||||
|
||||
build-linux-arm64:
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc go build -ldflags="$(LDFLAGS)" -o $(OUTDIR)/$(BINARY)-linux-arm64 .
|
||||
|
||||
build-all: build-linux-amd64 build-linux-arm64
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Install (systemd service)
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
install: build
|
||||
@echo "==> Installing ReceiptNext..."
|
||||
cp $(BINARY) /usr/local/bin/$(BINARY)
|
||||
@if [ ! -f /etc/$(BINARY)/.env ]; then \
|
||||
mkdir -p /etc/$(BINARY); \
|
||||
cp .env.example /etc/$(BINARY)/.env; \
|
||||
echo "==> Created /etc/$(BINARY)/.env — edit it with your credentials"; \
|
||||
fi
|
||||
@if [ ! -f /etc/systemd/system/$(BINARY).service ]; then \
|
||||
cp contrib/$(BINARY).service /etc/systemd/system/; \
|
||||
systemctl daemon-reload; \
|
||||
systemctl enable $(BINARY); \
|
||||
echo "==> Systemd service installed"; \
|
||||
fi
|
||||
@echo "==> Run 'systemctl start $(BINARY)' to start"
|
||||
@echo "==> Run 'systemctl status $(BINARY)' to check status"
|
||||
|
||||
uninstall:
|
||||
-systemctl stop $(BINARY) 2>/dev/null
|
||||
-systemctl disable $(BINARY) 2>/dev/null
|
||||
-rm -f /etc/systemd/system/$(BINARY).service
|
||||
-systemctl daemon-reload
|
||||
-rm -f /usr/local/bin/$(BINARY)
|
||||
@echo "==> ReceiptNext uninstalled"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Service management
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
run:
|
||||
./$(BINARY)
|
||||
|
||||
start:
|
||||
systemctl start $(BINARY)
|
||||
|
||||
stop:
|
||||
systemctl stop $(BINARY)
|
||||
|
||||
restart:
|
||||
systemctl restart $(BINARY)
|
||||
|
||||
logs:
|
||||
journalctl -u $(BINARY) -f
|
||||
|
||||
status:
|
||||
systemctl status $(BINARY)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# Clean
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
clean:
|
||||
rm -f $(BINARY)
|
||||
rm -rf $(OUTDIR)
|
||||
25
contrib/receiptnext.service
Normal file
25
contrib/receiptnext.service
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
[Unit]
|
||||
Description=ReceiptNext — AI-Powered Expense Tracker
|
||||
Documentation=https://git.lohmar.co.uk/cclohmar/ReceiptNext
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=receiptnext
|
||||
Group=receiptnext
|
||||
WorkingDirectory=/opt/receiptnext
|
||||
ExecStart=/usr/local/bin/receiptnext
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
EnvironmentFile=-/etc/receiptnext/.env
|
||||
StandardOutput=append:/var/log/receiptnext.log
|
||||
StandardError=append:/var/log/receiptnext.log
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
2
go.mod
2
go.mod
|
|
@ -1,4 +1,4 @@
|
|||
module github.com/expenseflow
|
||||
module github.com/cclohmar/ReceiptNext
|
||||
|
||||
go 1.22
|
||||
|
||||
|
|
|
|||
153
install.sh
Normal file
153
install.sh
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# install.sh — ReceiptNext one-command installer for bare-metal servers
|
||||
#
|
||||
# Usage:
|
||||
# curl -sL https://git.lohmar.co.uk/cclohmar/ReceiptNext/releases/download/v1.0.0/install.sh | bash
|
||||
#
|
||||
# Or from a local clone:
|
||||
# ./install.sh
|
||||
#
|
||||
# What it does:
|
||||
# 1. Creates receiptnext user if missing
|
||||
# 2. Creates /opt/receiptnext with templates/, static/, storage/
|
||||
# 3. Installs binary to /usr/local/bin/receiptnext
|
||||
# 4. Creates /etc/receiptnext/.env from .env.example (if not exists)
|
||||
# 5. Installs systemd service
|
||||
# 6. Starts the service
|
||||
# 7. Shows status and instructions
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BINARY="receiptnext"
|
||||
INSTALL_DIR="/opt/${BINARY}"
|
||||
BIN_DIR="/usr/local/bin"
|
||||
ETC_DIR="/etc/${BINARY}"
|
||||
SERVICE_FILE="contrib/${BINARY}.service"
|
||||
LOG_FILE="/var/log/${BINARY}.log"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
info() { echo -e "${GREEN}[✓]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
||||
error() { echo -e "${RED}[✗]${NC} $1"; }
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
error "Please run as root: sudo ./install.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " ╔═══════════════════════════════════════╗"
|
||||
echo " ║ ReceiptNext Installer ║"
|
||||
echo " ║ AI-Powered Expense Tracker ║"
|
||||
echo " ╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# ── Step 1: Build or download binary ──────────────────────────────
|
||||
if [ -f "./${BINARY}" ]; then
|
||||
info "Binary found locally"
|
||||
elif [ -f "go.mod" ]; then
|
||||
info "Building from source..."
|
||||
if ! command -v go &>/dev/null; then
|
||||
error "Go is required to build from source. Install go 1.22+ and re-run."
|
||||
exit 1
|
||||
fi
|
||||
go build -ldflags="-s -w" -o "${BINARY}" .
|
||||
info "Build complete"
|
||||
else
|
||||
# Try to download the latest release
|
||||
TAG=$(curl -s https://git.lohmar.co.uk/api/v1/repos/cclohmar/ReceiptNext/releases/latest \
|
||||
| python3 -c "import json,sys; print(json.load(sys.stdin).get('tag_name','v1.0.0'))" 2>/dev/null || echo "v1.0.0")
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64) ARCH="amd64" ;;
|
||||
aarch64) ARCH="arm64" ;;
|
||||
*) error "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
URL="https://git.lohmar.co.uk/cclohmar/ReceiptNext/releases/download/${TAG}/${BINARY}-linux-${ARCH}"
|
||||
info "Downloading ${URL}..."
|
||||
curl -sL -o "${BINARY}" "${URL}"
|
||||
info "Download complete"
|
||||
fi
|
||||
|
||||
chmod +x "${BINARY}"
|
||||
|
||||
# ── Step 2: Create system user ────────────────────────────────────
|
||||
if ! id -u ${BINARY} &>/dev/null 2>&1; then
|
||||
useradd -r -s /bin/false -d "${INSTALL_DIR}" ${BINARY}
|
||||
info "Created system user '${BINARY}'"
|
||||
fi
|
||||
|
||||
# ── Step 3: Create install directory ──────────────────────────────
|
||||
mkdir -p "${INSTALL_DIR}"/{templates,static/css,static/icons,storage}
|
||||
|
||||
# Copy templates and static assets
|
||||
cp -r templates/* "${INSTALL_DIR}/templates/"
|
||||
cp -r static/* "${INSTALL_DIR}/static/"
|
||||
cp -r storage/. "${INSTALL_DIR}/storage/" 2>/dev/null || true
|
||||
|
||||
# ── Step 4: Install binary ────────────────────────────────────────
|
||||
cp "${BINARY}" "${BIN_DIR}/${BINARY}"
|
||||
chmod +x "${BIN_DIR}/${BINARY}"
|
||||
info "Installed binary to ${BIN_DIR}/${BINARY}"
|
||||
|
||||
# ── Step 5: Create config directory ───────────────────────────────
|
||||
mkdir -p "${ETC_DIR}"
|
||||
if [ ! -f "${ETC_DIR}/.env" ]; then
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example "${ETC_DIR}/.env"
|
||||
info "Created ${ETC_DIR}/.env — edit it with your credentials"
|
||||
else
|
||||
warn "No .env.example found — create ${ETC_DIR}/.env manually"
|
||||
fi
|
||||
else
|
||||
info "${ETC_DIR}/.env already exists — keeping existing config"
|
||||
fi
|
||||
|
||||
# ── Step 6: Install systemd service ───────────────────────────────
|
||||
if [ -f "${SERVICE_FILE}" ]; then
|
||||
cp "${SERVICE_FILE}" /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable ${BINARY}
|
||||
info "Systemd service installed and enabled"
|
||||
fi
|
||||
|
||||
# ── Step 7: Set permissions ───────────────────────────────────────
|
||||
chown -R ${BINARY}:${BINARY} "${INSTALL_DIR}"
|
||||
chown ${BINARY}:${BINARY} "${ETC_DIR}/.env" 2>/dev/null || true
|
||||
touch "${LOG_FILE}" && chown ${BINARY}:${BINARY} "${LOG_FILE}"
|
||||
|
||||
# ── Step 8: Start service ─────────────────────────────────────────
|
||||
systemctl start ${BINARY} || warn "Service failed to start — check 'systemctl status ${BINARY}'"
|
||||
sleep 2
|
||||
|
||||
echo ""
|
||||
echo " ╔═══════════════════════════════════════╗"
|
||||
echo " ║ Installation Complete! ║"
|
||||
echo " ╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo " Config: ${ETC_DIR}/.env"
|
||||
echo " Binary: ${BIN_DIR}/${BINARY}"
|
||||
echo " Data: ${INSTALL_DIR}"
|
||||
echo " Logs: ${LOG_FILE}"
|
||||
echo " Service: systemctl status ${BINARY}"
|
||||
echo " Restart: systemctl restart ${BINARY}"
|
||||
echo " Log tail: journalctl -u ${BINARY} -f"
|
||||
echo ""
|
||||
|
||||
if systemctl is-active --quiet ${BINARY}; then
|
||||
info "ReceiptNext is running!"
|
||||
echo " Open http://YOUR_SERVER_IP:8080 in your browser"
|
||||
echo ""
|
||||
echo " First steps:"
|
||||
echo " 1. Edit ${ETC_DIR}/.env with your SMTP + AI credentials"
|
||||
echo " 2. Restart: systemctl restart ${BINARY}"
|
||||
echo ""
|
||||
else
|
||||
warn "Service is not running. Check: systemctl status ${BINARY}"
|
||||
fi
|
||||
|
|
@ -12,10 +12,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/expenseflow/internal/auth"
|
||||
"github.com/expenseflow/internal/database"
|
||||
"github.com/expenseflow/internal/email"
|
||||
"github.com/expenseflow/internal/utils"
|
||||
"github.com/cclohmar/ReceiptNext/internal/auth"
|
||||
"github.com/cclohmar/ReceiptNext/internal/database"
|
||||
"github.com/cclohmar/ReceiptNext/internal/email"
|
||||
"github.com/cclohmar/ReceiptNext/internal/utils"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/expenseflow/internal/database"
|
||||
"github.com/expenseflow/internal/utils"
|
||||
"github.com/cclohmar/ReceiptNext/internal/database"
|
||||
"github.com/cclohmar/ReceiptNext/internal/utils"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/expenseflow/internal/ai"
|
||||
"github.com/expenseflow/internal/database"
|
||||
"github.com/expenseflow/internal/utils"
|
||||
"github.com/cclohmar/ReceiptNext/internal/ai"
|
||||
"github.com/cclohmar/ReceiptNext/internal/database"
|
||||
"github.com/cclohmar/ReceiptNext/internal/utils"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import (
|
|||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
|
||||
"github.com/expenseflow/internal/database"
|
||||
"github.com/expenseflow/internal/email"
|
||||
"github.com/cclohmar/ReceiptNext/internal/database"
|
||||
"github.com/cclohmar/ReceiptNext/internal/email"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
8
main.go
8
main.go
|
|
@ -22,10 +22,10 @@ import (
|
|||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/expenseflow/internal/auth"
|
||||
"github.com/expenseflow/internal/database"
|
||||
"github.com/expenseflow/internal/email"
|
||||
"github.com/expenseflow/internal/handlers"
|
||||
"github.com/cclohmar/ReceiptNext/internal/auth"
|
||||
"github.com/cclohmar/ReceiptNext/internal/database"
|
||||
"github.com/cclohmar/ReceiptNext/internal/email"
|
||||
"github.com/cclohmar/ReceiptNext/internal/handlers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue