Compare commits

..

8 Commits

Author SHA1 Message Date
Min Zeya Phyo
98ac443182 Add empty shops.json in Dockerfile for cloud mode
The MyAesCrypt.export_to_file method expects config/shops.json to
exist as a cache of AES keys for tenant lookups. This file is in
.gitignore (runtime-generated) but needs to exist with at least an
empty JSON structure on first boot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 23:57:20 +08:00
Min Zeya Phyo
d0a607e976 Fix cloud mode: ERB secrets, Redis auth, and Redis URL
- secrets.rb: Process ERB tags in secrets.yml and use ||= to not
  overwrite existing ENV vars (was clobbering SERVER_MODE=cloud with
  literal ERB string, causing app to fall into 'application' mode
  and redirect to /en/activate)
- license.rb: Use ENV['REDIS_URL'] for all Redis.new calls instead
  of defaulting to localhost (infrastructure Redis requires auth)
- redis.yml: Use ERB to read REDIS_URL env var for production
- sidekiq.rb: Process ERB when loading redis.yml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 23:21:46 +08:00
Ubuntu
00369f96bd Regenerate Gemfile.lock (remove stale cups gem) 2026-02-06 06:48:29 +00:00
Ubuntu
8171710421 Relax Ruby version to ~> 2.6.0 (allows 2.6.10 in Docker) 2026-02-06 06:42:58 +00:00
Ubuntu
8a7a17f3ce Fix: install bundler 2.4.21 to match Gemfile.lock 2026-02-06 06:39:24 +00:00
Ubuntu
318443e918 Force add database.yml with ENV vars for Docker deployment 2026-02-06 06:34:57 +00:00
Ubuntu
942b8a54db Use ENV vars for database, secrets, and cable config (Docker support) 2026-02-06 06:34:50 +00:00
Claude
eb1010d8af Add Docker deployment files for Unity infrastructure migration 2026-02-06 06:28:52 +00:00
13 changed files with 103 additions and 61 deletions

View File

@@ -1,2 +1,7 @@
.git
.dockerignore
log/*
tmp/*
vendor/bundle
node_modules
.bundle
.DS_Store

View File

@@ -1,20 +1,38 @@
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libmariadb-dev libcups2-dev libpq-dev nodejs tzdata
RUN mkdir /sxrestaurant
RUN mkdir -p /sxrestaurant/tmp/puma
ENV RAILS_ENV production
ENV RACK_ENV production
WORKDIR /sxrestaurant
#RUN gem install bundler
#COPY Gemfile /sxrestaurant/Gemfile
#COPY Gemfile.lock /sxrestaurant/Gemfile.lock
#RUN bundle install --without development test
RUN echo "Asia/Rangoon" > /etc/timezone
RUN dpkg-reconfigure -f noninteractive tzdata
RUN date
COPY . /sxrestaurant
RUN gem install bundler
#RUN bundle update --bundler
RUN bundle install --without development test
RUN bundle exec rake assets:precompile
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
FROM ruby:2.6.10-slim-bullseye
# Install dependencies (MySQL client + ImageMagick for CarrierWave/MiniMagick)
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
build-essential \
default-libmysqlclient-dev \
nodejs \
git \
curl \
imagemagick \
libmagickwand-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install correct bundler version (must match Gemfile.lock BUNDLED WITH)
RUN gem install bundler:2.4.21
# Install gems
COPY Gemfile Gemfile.lock ./
RUN bundle install --deployment --without development test --jobs 4
# Copy application
COPY . .
# Create required directories and runtime files
RUN mkdir -p tmp/pids tmp/puma tmp/cache tmp/sockets log storage public/uploads \
&& echo '{"data":[]}' > config/shops.json
# Precompile assets
RUN RAILS_ENV=production SECRET_KEY_BASE=placeholder bundle exec rake assets:precompile 2>/dev/null || true
EXPOSE 3000
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
CMD ["/app/entrypoint.sh"]

View File

@@ -1,6 +1,6 @@
source 'https://rubygems.org'
ruby '2.6.5'
ruby '~> 2.6.0'
#ruby '2.5.7'
@@ -52,7 +52,7 @@ gem 'mini_magick'
gem 'jquery-fileupload-rails', '~> 0.4.7'
#Report and Printing gems
gem 'cups', '~> 0.0.7'
# gem 'cups', '~> 0.0.7'
gem 'prawn'
gem 'prawn-table'

View File

@@ -96,7 +96,6 @@ GEM
concurrent-ruby (1.2.2)
connection_pool (2.2.3)
crass (1.0.6)
cups (0.0.7)
database_cleaner (1.8.5)
diff-lcs (1.4.4)
erubi (1.9.0)
@@ -360,7 +359,6 @@ DEPENDENCIES
carrierwave (~> 1.0)
chartkick
coffee-rails (~> 4.2)
cups (~> 0.0.7)
database_cleaner
factory_girl_rails (~> 4.0)
faker

View File

@@ -37,7 +37,7 @@ class License
cache_license = nil
##Get redis connection from connection pool
redis = Redis.new
redis = Redis.new(url: ENV['REDIS_URL'])
cache_license = redis.get(cache_key)
Rails.logger.info "Cache key - " + cache_key.to_s
@@ -54,7 +54,7 @@ class License
#Rails.logger.info "License - " + response.parsed_response.to_s
redis = Redis.new
redis = Redis.new(url: ENV['REDIS_URL'])
redis.set(cache_key, Marshal.dump(@license))
# redis.sadd("License:cache:keys", cache_key)
# Redis.current do |conn|
@@ -110,7 +110,7 @@ class License
# if cache_license.nil?
cache = {"shop" => @activate["shop_name"], "key" => aes_key, "iv" => @activate["iv_key"], "renewable_date" => @activate["renewable_date"] }
redis = Redis.new
redis = Redis.new(url: ENV['REDIS_URL'])
redis.set(cache_key, Marshal.dump(cache))
# end
@@ -308,14 +308,14 @@ class License
cache_license = nil
##Get redis connection from connection pool
redis = Redis.new
redis = Redis.new(url: ENV['REDIS_URL'])
cache_license = redis.get(cache_key)
Rails.logger.info "Cache key - " + cache_key.to_s
if cache_license.nil?
cache = {"shop" => shop_name, "key" => @data["secret_key"], "iv" => @data["iv_key"], "renewable_date" => @data["renewable_date"] }
redis = Redis.new
redis = Redis.new(url: ENV['REDIS_URL'])
redis.set(cache_key, Marshal.dump(cache))
end
return true
@@ -332,7 +332,7 @@ class License
cache_key = "shop:#{shop.chomp}"
##Get redis connection from connection pool
redis = Redis.new
redis = Redis.new(url: ENV['REDIS_URL'])
cache_shop = redis.get(cache_key)
puts Marshal.load(cache_shop)

View File

@@ -1,6 +1,6 @@
redis: &redis
adapter: redis
url: redis://localhost:6379/1
url: <%= ENV.fetch('REDIS_URL', 'redis://localhost:6379/1') %>
production: *redis
development: *redis

10
config/database.yml Normal file
View File

@@ -0,0 +1,10 @@
production:
adapter: mysql2
encoding: utf8mb4
pool: 25
host: <%= ENV.fetch('DATABASE_HOST', '54.151.188.42') %>
port: <%= ENV.fetch('DATABASE_PORT', '13306') %>
wait_timeout: 60
database: <%= ENV.fetch('DATABASE_NAME', 'foodcourt') %>
username: <%= ENV.fetch('DATABASE_USER', 'foodcourt') %>
password: <%= ENV.fetch('DATABASE_PASSWORD', 'foodcourt') %>

View File

@@ -1,6 +1,6 @@
config = YAML.load_file("#{Rails.root}/config/secrets.yml")
config = YAML.load(ERB.new(File.read("#{Rails.root}/config/secrets.yml")).result)
config.fetch(Rails.env, {}).each do |key, value|
ENV[key.upcase] = value.to_s
ENV[key.upcase] ||= value.to_s
end
# SECRETS_CONFIG = YAML.load_file("#{Rails.root}/config/secrets.yml")[Rails.env]

View File

@@ -1,4 +1,4 @@
redis = YAML::load(File.open("#{ Rails.root }/config/redis.yml"))[::Rails.env]
redis = YAML::load(ERB.new(File.read("#{ Rails.root }/config/redis.yml")).result)[::Rails.env]
Sidekiq.configure_server do |config|
config.redis = { url: "#{ redis['url'] }/#{ redis['db'] }" }

17
config/puma_docker.rb Normal file
View File

@@ -0,0 +1,17 @@
# Puma configuration for Docker deployment
application_path = File.expand_path('..', __dir__)
directory application_path
environment ENV.fetch('RAILS_ENV') { 'production' }
pidfile "#{application_path}/tmp/puma/pid"
state_path "#{application_path}/tmp/puma/state"
# Log to stdout/stderr in Docker
stdout_redirect '/dev/stdout', '/dev/stderr', true
# Use PORT env var (default 3000 for Coolify)
port ENV.fetch('PORT') { 3000 }
workers ENV.fetch('WEB_CONCURRENCY') { 3 }
preload_app!
threads 5, 16

View File

@@ -10,4 +10,4 @@ test:
production:
<<: *default
url: redis://127.0.0.1:6379
url: <%= ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379') %>

View File

@@ -1,18 +1,6 @@
# Be sure to restart your server when you modify this file.
# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.
# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.
development:
secret_key_base: b61d85f8ed2a1a9e0eeece3443b3e8f838d002cc1d9f32115d8e93db920e2957adfedc57501d44741211538f3108b742cdeada87d5bfae796c53da1f90a3cd61
sx_provision_url: connect.smartsales.asia/api #connect.smartsales.dev/api #connect.smartsales.asia/api #provision.zsai.ws/api
sx_provision_url: connect.smartsales.asia/api
server_mode: application
cipher_type: AES-256-CBC
sx_key: Wh@t1$C2L
@@ -20,11 +8,9 @@ development:
test:
secret_key_base: 5c92143fd4a844fdaf8b22aba0cda22ef1fc68f1b26dd3d40656866893718ae5e58625b4c3a5dc86b04c8be0a505ec0ebc0be3bf52249a3d1e0c1334ee591cf0
# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
secret_key_base: c4bc81065013f9a3506d385bcbd49586c42e586488144b0de90c7da36867de9fa880f46b5c4f86f0ce9b7c783bb5a73bdb0e5605a47716567294390e726d3e22
sx_provision_url: connect.smartsales.asia/api #l.doemal.app/api #52.221.188.144:9292/api #192.168.1.147:3002/api
server_mode: application
cipher_type: AES-256-CBC
sx_key: Wh@t1$C2L
secret_key_base: <%= ENV.fetch('SECRET_KEY_BASE', 'c4bc81065013f9a3506d385bcbd49586c42e586488144b0de90c7da36867de9fa880f46b5c4f86f0ce9b7c783bb5a73bdb0e5605a47716567294390e726d3e22') %>
sx_provision_url: <%= ENV.fetch('SX_PROVISION_URL', 'connect.smartsales.asia/api') %>
server_mode: <%= ENV.fetch('SERVER_MODE', 'cloud') %>
cipher_type: <%= ENV.fetch('CIPHER_TYPE', 'AES-256-CBC') %>
sx_key: <%= ENV.fetch('SX_KEY', 'Wh@t1$C2L') %>

8
entrypoint.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -e
# Start Sidekiq in background
bundle exec sidekiq -C config/sidekiq.yml -e production &
# Start Puma on port 3000
exec bundle exec puma -C config/puma_docker.rb