From aab5acafb41ce2b48767ec2dc295e487ad0c836e Mon Sep 17 00:00:00 2001 From: Gregory Ballantine Date: Fri, 29 Aug 2025 02:44:43 -0400 Subject: [PATCH] Initial project structure with some barebones layout, CSS and JS from game-data --- .gitignore | 12 +++ Dockerfile.dev | 18 ++++ Dockerfile.gulp | 13 +++ Gemfile | 21 +++++ Gemfile.lock | 129 +++++++++++++++++++++++++++++ LICENSE | 2 +- Rakefile | 23 +++++ assets/scripts/tirannwn.coffee | 3 + assets/styles/lletya.sass | 33 ++++++++ bin/docker-build.bat | 2 + bin/docker-build.sh | 5 ++ bin/docker-rake.bat | 1 + bin/docker-rake.sh | 3 + bin/docker-run.bat | 2 + bin/docker-run.sh | 5 ++ bin/setup-dev.sh | 6 ++ config.ru | 5 ++ config/defaults.yaml | 3 + config/puma.rb | 11 +++ data/.gitkeep | 0 gulpfile.js | 57 +++++++++++++ package.json | 28 +++++++ public/.gitkeep | 0 src/appinfo.rb | 7 ++ src/config.rb | 29 +++++++ src/controllers/base_controller.rb | 15 ++++ src/controllers/index.rb | 14 ++++ src/helpers.rb | 31 +++++++ src/server.rb | 27 ++++++ views/layout.erb | 29 +++++++ views/partials/footer.erb | 10 +++ views/partials/navbar.erb | 20 +++++ views/toplevel/dashboard.erb | 1 + 33 files changed, 564 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.dev create mode 100644 Dockerfile.gulp create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile create mode 100644 assets/scripts/tirannwn.coffee create mode 100644 assets/styles/lletya.sass create mode 100644 bin/docker-build.bat create mode 100755 bin/docker-build.sh create mode 100644 bin/docker-rake.bat create mode 100755 bin/docker-rake.sh create mode 100644 bin/docker-run.bat create mode 100755 bin/docker-run.sh create mode 100755 bin/setup-dev.sh create mode 100644 config.ru create mode 100644 config/defaults.yaml create mode 100644 config/puma.rb create mode 100644 data/.gitkeep create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 public/.gitkeep create mode 100644 src/appinfo.rb create mode 100644 src/config.rb create mode 100644 src/controllers/base_controller.rb create mode 100644 src/controllers/index.rb create mode 100644 src/helpers.rb create mode 100644 src/server.rb create mode 100644 views/layout.erb create mode 100644 views/partials/footer.erb create mode 100644 views/partials/navbar.erb create mode 100644 views/toplevel/dashboard.erb diff --git a/.gitignore b/.gitignore index d6aa672..ed547b8 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,15 @@ build-iPhoneSimulator/ # Used by RuboCop. Remote config files pulled in from inherit_from directive. # .rubocop-https?--* +# Ignore local data +data/ + +# Node modules +node_modules/ + +# Compiled assets +public/*/ + +# Ignore production configuration files to protect against credential leaks +config/production.yaml + diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..71041bf --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,18 @@ +FROM ruby:3.4 + +RUN gem install bundler + +WORKDIR /usr/src/muldap + +COPY Gemfile Gemfile.l*ck ./ + +RUN bundle check || bundle install + +RUN gem install rake + +COPY . ./ + +ENV RACK_ENV=development + +ENTRYPOINT ["bundle", "exec", "puma"] + diff --git a/Dockerfile.gulp b/Dockerfile.gulp new file mode 100644 index 0000000..aa48184 --- /dev/null +++ b/Dockerfile.gulp @@ -0,0 +1,13 @@ +# Node.js runtime +FROM node:24 + +WORKDIR /usr/src/muldap/ + +COPY package.* /usr/src/muldap/ + +RUN npm install + +VOLUME /usr/src/muldap/node_modules/ + +# Run the app +CMD [ "npm", "run", "gulp" ] diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..da773dd --- /dev/null +++ b/Gemfile @@ -0,0 +1,21 @@ +source 'https://rubygems.org' + +gem 'sinatra', '~> 4.1' +gem 'sinatra-contrib', '~> 4.1' +gem 'puma', '~> 6.6' + +gem 'logger' + +group :development, :test do + gem 'rerun' + gem 'wdm', '>= 0.1.0' if Gem.win_platform? + + # rubocop and extensions for code style + gem 'rubocop' + gem 'rubocop-rspec' +end + +group :test do + gem 'rspec' + gem 'rack-test' +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..f20a8da --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,129 @@ +GEM + remote: https://rubygems.org/ + specs: + ast (2.4.3) + base64 (0.3.0) + diff-lcs (1.6.2) + ffi (1.17.2) + ffi (1.17.2-aarch64-linux-gnu) + ffi (1.17.2-aarch64-linux-musl) + ffi (1.17.2-arm-linux-gnu) + ffi (1.17.2-arm-linux-musl) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86-linux-gnu) + ffi (1.17.2-x86-linux-musl) + ffi (1.17.2-x86_64-darwin) + ffi (1.17.2-x86_64-linux-gnu) + ffi (1.17.2-x86_64-linux-musl) + json (2.13.2) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + logger (1.7.0) + multi_json (1.17.0) + mustermann (3.0.4) + ruby2_keywords (~> 0.0.1) + nio4r (2.7.4) + parallel (1.27.0) + parser (3.3.9.0) + ast (~> 2.4.1) + racc + prism (1.4.0) + puma (6.6.1) + nio4r (~> 2.0) + racc (1.8.1) + rack (3.2.0) + rack-protection (4.1.1) + base64 (>= 0.1.0) + logger (>= 1.6.0) + rack (>= 3.0.0, < 4) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rainbow (3.1.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + regexp_parser (2.11.2) + rerun (0.14.0) + listen (~> 3.0) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.5) + rubocop (1.80.1) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.46.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-rspec (3.6.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + sinatra (4.1.1) + logger (>= 1.6.0) + mustermann (~> 3.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.1.1) + rack-session (>= 2.0.0, < 3) + tilt (~> 2.0) + sinatra-contrib (4.1.1) + multi_json (>= 0.0.2) + mustermann (~> 3.0) + rack-protection (= 4.1.1) + sinatra (= 4.1.1) + tilt (~> 2.0) + tilt (2.6.1) + unicode-display_width (3.1.5) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + ruby + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + logger + puma (~> 6.6) + rack-test + rerun + rspec + rubocop + rubocop-rspec + sinatra (~> 4.1) + sinatra-contrib (~> 4.1) + +BUNDLED WITH + 2.6.9 diff --git a/LICENSE b/LICENSE index ddd1996..6bdbca4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2025 gballan. +Copyright (c) 2025 Metaunix.net. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..0a8e173 --- /dev/null +++ b/Rakefile @@ -0,0 +1,23 @@ +require 'bundler/setup' + +namespace :server do + task :start do + ENV['RACK_ENV'] = 'production' + system("puma") + end + + task :dev do + system('rerun --no-notify --dir="src/" puma') + end +end + +namespace :test do + task :unit do + ENV['RACK_ENV'] = 'testing' + system("rspec") + end + + task :lint do + system("rubocop src/ spec/") + end +end diff --git a/assets/scripts/tirannwn.coffee b/assets/scripts/tirannwn.coffee new file mode 100644 index 0000000..443d9a5 --- /dev/null +++ b/assets/scripts/tirannwn.coffee @@ -0,0 +1,3 @@ +$ -> + # let us know when javascript is running. + console.log('Ready.') diff --git a/assets/styles/lletya.sass b/assets/styles/lletya.sass new file mode 100644 index 0000000..b6b84e6 --- /dev/null +++ b/assets/styles/lletya.sass @@ -0,0 +1,33 @@ +@use "sass:color" + +//$primary-color: #3399ff +$primary-color: orangered +$primary-color-highlight: color.adjust($primary-color, $lightness: -10%) + +html + width: 100% + height: 100% + +body + background: rgb(240, 235, 248) + +table + border: 1px solid #666 + +a + transition: color 220ms ease-in-out + +#wrapper + background: white + padding: 1.5rem 2rem + border: 1px solid #bbb + border-radius: 8px + +#main-nav + li + a + font-size: 1.25rem + +#site-title + img + max-height: 40px diff --git a/bin/docker-build.bat b/bin/docker-build.bat new file mode 100644 index 0000000..e9b348c --- /dev/null +++ b/bin/docker-build.bat @@ -0,0 +1,2 @@ +docker build -t muldap -f Dockerfile.dev . +docker build -t muldap-gulp -f Dockerfile.gulp . diff --git a/bin/docker-build.sh b/bin/docker-build.sh new file mode 100755 index 0000000..772a53a --- /dev/null +++ b/bin/docker-build.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +docker build -t muldap -f Dockerfile.dev . +docker build -t muldap-gulp -f Dockerfile.gulp . + diff --git a/bin/docker-rake.bat b/bin/docker-rake.bat new file mode 100644 index 0000000..971afd5 --- /dev/null +++ b/bin/docker-rake.bat @@ -0,0 +1 @@ +docker exec muldap rake %* diff --git a/bin/docker-rake.sh b/bin/docker-rake.sh new file mode 100755 index 0000000..2659f67 --- /dev/null +++ b/bin/docker-rake.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +docker exec muldap rake "$@" diff --git a/bin/docker-run.bat b/bin/docker-run.bat new file mode 100644 index 0000000..2a95ec5 --- /dev/null +++ b/bin/docker-run.bat @@ -0,0 +1,2 @@ +docker run --rm -d -t -v "%cd%:/usr/src/muldap" -p 9300:9300 --name muldap muldap +docker run --rm -d -t -v "%cd%:/usr/src/muldap" --name muldap-gulp muldap-gulp npm run gulp watch diff --git a/bin/docker-run.sh b/bin/docker-run.sh new file mode 100755 index 0000000..4ff146d --- /dev/null +++ b/bin/docker-run.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +docker run --rm -d -t -v "$(pwd):/usr/src/muldap" -p 9300:9300 --name muldap muldap +docker run --rm -d -t -v "$(pwd):/usr/src/muldap" --name muldap-gulp muldap-gulp npm run gulp watch + diff --git a/bin/setup-dev.sh b/bin/setup-dev.sh new file mode 100755 index 0000000..4771b7f --- /dev/null +++ b/bin/setup-dev.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# Configure bundler to use local vendor path +bundle config set --local path 'vendor/bundle' +# Install gems +bundle install diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..d24c0ba --- /dev/null +++ b/config.ru @@ -0,0 +1,5 @@ +# Load application config + +root = ::File.dirname(__FILE__) +require ::File.join( root, 'src', 'server' ) +run GameData.new diff --git a/config/defaults.yaml b/config/defaults.yaml new file mode 100644 index 0000000..f641ad5 --- /dev/null +++ b/config/defaults.yaml @@ -0,0 +1,3 @@ +server: + host: '0.0.0.0' + port: '9300' diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..f071a4f --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,11 @@ +app_dir = File.expand_path('..', __dir__) +directory app_dir + +environment ENV.fetch('RACK_ENV', 'development') + +require_relative '../src/config' +conf = Config.new() + +bind "tcp://#{conf.get('server.host')}:#{conf.get('server.port')}" +workers 2 +threads 1, 5 diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..ab3df1e --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,57 @@ +const gulp = require('gulp'); +const sass = require('gulp-sass')(require('sass')); +const coffee = require('gulp-coffee'); +const sourcemaps = require('gulp-sourcemaps'); +const plumber = require('gulp-plumber'); + +// Compile .sass files to compressed CSS +function compileSass() { + return gulp.src('assets/styles/**/*.sass') + .pipe(plumber()) + .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError)) + .pipe(gulp.dest('public/css')); +} + +// Compile .coffee files to JavaScript with source maps +function compileCoffee() { + return gulp.src('assets/scripts/**/*.coffee', { sourcemaps: true }) + .pipe(plumber()) + .pipe(coffee({ bare: true })) + .pipe(gulp.dest('public/js', { sourcemaps: '.' })); +} + +// Copy image files to public/img/ +function copyImages() { + return gulp.src('assets/img/**/*', {encoding: false}) + .pipe(gulp.dest('public/img/')); +} + +// Watch files for changes +function watchFiles(cb) { + gulp.watch('assets/styles/**/*.sass', compileSass); + gulp.watch('assets/scripts/**/*.coffee', compileCoffee); + gulp.watch('assets/img/**/*', copyImages) + cb(); +} + +// Chain all asset builds together +function buildAssets() { + return gulp.parallel(compileSass, compileCoffee, copyImages); +} + +// Perform initial build then watch +function buildAndWatch() { + return gulp.series( + buildAssets(), + watchFiles + ); +} + +// one-time build +exports.build = buildAssets(); + +// start builds with file watching +exports.watch = buildAndWatch(); + +// Define default task (defaults to watch) +exports.default = exports.watch; diff --git a/package.json b/package.json new file mode 100644 index 0000000..0356246 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "muldap", + "description": "Self-service frontend for LDAP users.", + "version": "0.1.0", + "main": "src/server.rb", + "scripts": { + "gulp": "gulp" + }, + "repository": { + "type": "git", + "url": "https://git.metaunix.net/Metaunix/muldap" + }, + "keywords": [ + "ldap", + "user", + "account" + ], + "author": "Gregory Ballantine ", + "license": "BSD-3-Clause", + "devDependencies": { + "gulp": "^5.0.1", + "gulp-coffee": "^3.0.3", + "gulp-plumber": "^1.2.1", + "gulp-sass": "^6.0.1", + "gulp-sourcemaps": "^3.0.0", + "sass": "^1.89.2" + } +} diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/appinfo.rb b/src/appinfo.rb new file mode 100644 index 0000000..907b7ff --- /dev/null +++ b/src/appinfo.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module AppInfo + + VERSION = '0.1.0' + +end diff --git a/src/config.rb b/src/config.rb new file mode 100644 index 0000000..1fda1ec --- /dev/null +++ b/src/config.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'yaml' + +# Config - loads and manages the app's configuration +class Config + + DEFAULT_CONFIG = 'config/defaults.yaml' + ENVIRONMENT_CONFIG = ENV.fetch('RACK_ENV', 'development') + + def initialize(config_path = "config/#{ENVIRONMENT_CONFIG}.yaml") + @data = YAML.load_file(DEFAULT_CONFIG) + + # merge in user-defined configuration if it exists + @data.merge!(YAML.load_file(config_path)) if File.exist?(config_path) + end + + def get(key) + bits = key.split('.') + value = @data + + bits.each do |bit| + value = value[bit] + end + + return value + end + +end diff --git a/src/controllers/base_controller.rb b/src/controllers/base_controller.rb new file mode 100644 index 0000000..df010ea --- /dev/null +++ b/src/controllers/base_controller.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'sinatra/base' + +# BaseController - base modular Sinatra app class +class BaseController < Sinatra::Base + + # Register view helpers + require_relative '../helpers' + helpers Helpers + + # Set up our view engine + set :views, File.join(settings.root, '/../../views') + +end diff --git a/src/controllers/index.rb b/src/controllers/index.rb new file mode 100644 index 0000000..b1e2f7f --- /dev/null +++ b/src/controllers/index.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require_relative 'base_controller' + +# / (top-level) routes +class IndexController < BaseController + + get '/' do + erb :'toplevel/dashboard', locals: { + title: 'Dashboard' + } + end + +end diff --git a/src/helpers.rb b/src/helpers.rb new file mode 100644 index 0000000..bd318f8 --- /dev/null +++ b/src/helpers.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require_relative 'appinfo' + +# Helpers - view helper functions +module Helpers + + def ruby_version + return RUBY_VERSION + end + + def app_version + return AppInfo::VERSION + end + + def date_format(date) + dt = date.to_datetime + return dt.strftime('%B %d, %Y @ %I:%M:%S %p %Z') + end + + def average_array(arr, decimals = 1) + sum = 0 + + arr.each do |i| + sum += i + end + + return (sum / arr.length).round(decimals) + end + +end diff --git a/src/server.rb b/src/server.rb new file mode 100644 index 0000000..9c0be4c --- /dev/null +++ b/src/server.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'sinatra/base' + +require_relative 'config' + +# Load configuration from environment config file +$conf = Config.new() + +# Load in routes +require_relative 'controllers/index' + +# GameData - main app that gets launched +# - inherits from Sinatra::Base to instantiate the server +# - sets up some base app configuration +# - registers route classes with the base app +class GameData < Sinatra::Base + + enable :sessions + + # Set up static file serving + enable :static + set :public_folder, File.join(__dir__, '/../public') + + use IndexController + +end diff --git a/views/layout.erb b/views/layout.erb new file mode 100644 index 0000000..ef44f7b --- /dev/null +++ b/views/layout.erb @@ -0,0 +1,29 @@ + + + + + + + + <%= title %> | MULDAP + + + + + + + + + <%= erb :'partials/navbar', :locals => locals %> + + +
+
+ <%= yield %> +
+
+ + + <%= erb :'partials/footer', :locals => locals %> + + diff --git a/views/partials/footer.erb b/views/partials/footer.erb new file mode 100644 index 0000000..f3ce188 --- /dev/null +++ b/views/partials/footer.erb @@ -0,0 +1,10 @@ +
+
+
+
+

MULDAP version v<%= app_version() %>

+

Running Ruby version v<%= ruby_version() %>

+
+
+
+
\ No newline at end of file diff --git a/views/partials/navbar.erb b/views/partials/navbar.erb new file mode 100644 index 0000000..a259b86 --- /dev/null +++ b/views/partials/navbar.erb @@ -0,0 +1,20 @@ + diff --git a/views/toplevel/dashboard.erb b/views/toplevel/dashboard.erb new file mode 100644 index 0000000..2d64910 --- /dev/null +++ b/views/toplevel/dashboard.erb @@ -0,0 +1 @@ +

This is the MULDAP dashboard.

\ No newline at end of file