changeset 1995:301a6561fb6b default tip

add host/admin
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 04 Jul 2025 10:25:38 -0600
parents 035996323891
children
files .hgignore host/admin/push-local.sh host/admin/push.sh host/admin/read_me.txt host/admin/serve.sh host/admin/src/hi.luan host/admin/src/init.luan host/admin/src/private/Config_sample.luan host/admin/src/private/host/Config.luan host/admin/src/private/lib/Utils.luan host/admin/src/private/lib/monitor.luan host/admin/src/private/tools/admin.html.luan host/admin/src/private/tools/clean_logs.luan host/admin/src/private/tools/java_threads.html.luan host/admin/src/private/tools/links.html host/admin/src/private/tools/luan_threads.html.luan host/admin/src/private/tools/request.txt.luan host/admin/src/private/tools/run.luan host/admin/src/private/tools/shell.html.luan host/admin/src/private/tools/sites.html.luan
diffstat 20 files changed, 485 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Thu Jul 03 22:23:58 2025 -0600
+++ b/.hgignore	Fri Jul 04 10:25:38 2025 -0600
@@ -18,3 +18,4 @@
 backup/backups.copy/
 mine/
 private/Config.luan
+changeset.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/push-local.sh	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -e
+
+hg identify >src/private/changeset.txt
+
+PASSWORD="$(luan 'string:require("luan:Io.luan").stdout.write(require("file:src/private/Config.luan").push_password)')"
+
+luan luan:host/push.luan admin.me.luan.software "$PASSWORD" src 2>&1 | tee err
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/push.sh	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -e
+
+hg identify >src/private/changeset.txt
+
+PASSWORD="$(luan 'string:require("luan:Io.luan").stdout.write(require("file:src/private/Config.luan").push_password)')"
+
+luan luan:host/push.luan admin.s1.luan.software "$PASSWORD" src 2>&1 | tee err
+luan luan:host/push.luan admin.s2.luan.software "$PASSWORD" src 2>&1 | tee err
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/read_me.txt	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,4 @@
+To configure, go to src/private and copy Config_sample.luan to Config.luan .  Then update Config.luan as needed.
+
+If you have access, you can get Config.luan here:
+https://hg.reactionary.software/repo/config/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/serve.sh	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+luan luan:http/serve.luan src 2>&1 | tee err
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/hi.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,8 @@
+local Io = require "luan:Io.luan"
+local Http = require "luan:http/Http.luan"
+
+
+return function()
+	Io.stdout = Http.response.text_writer()
+	%>hi<%
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/init.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,10 @@
+local Http = require "luan:http/Http.luan"
+local Hosted = require "luan:host/Hosted.luan"
+
+Hosted.set_https and Hosted.set_https(true)
+
+require "site:/private/lib/monitor.luan"
+
+Http.dont_gc and Http.dont_gc()
+
+return true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/Config_sample.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,6 @@
+return {
+	email_to = "x@x.com"
+	mail_password = "xxx"
+	hosting_password = "xxx"
+	push_password = "xxx"
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/host/Config.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,33 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local Io = require "luan:Io.luan"
+local String = require "luan:String.luan"
+local regex = String.regex or error()
+require "java"
+local BackupIndexWriter = require "java:goodjava.lucene.backup.BackupIndexWriter"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "host/Config"
+
+local this = (...)
+local base = regex("^(.*)\Q/host/Config.luan\E$").match(this) or error()
+local Admin_config = require(base.."/Config.luan")
+
+
+local Config = {}
+
+Config.password = Admin_config.hosting_password or error()
+Config.old_password = "password"
+
+local my_ips = Io.my_ips()
+if my_ips[Io.ip("s1.luan.software")]==true then
+	BackupIndexWriter.backupDomains = {"backup.luan.software"}
+elseif my_ips[Io.ip("s2.luan.software")]==true then
+	BackupIndexWriter.backupDomains = {"backup.luan.software"}
+else
+	local dir = Io.uri("file:.").canonical().to_string()
+	if String.starts_with( dir, "/Users/fschmidt/" ) then
+		BackupIndexWriter.backupDomains = {"localhost"}
+	end
+end
+
+return Config
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/lib/Utils.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,77 @@
+local Config = require "site:/private/Config.luan"
+local Hosted = require "luan:host/Hosted.luan"
+Hosted.no_security and Hosted.no_security(Config.hosting_password)
+require "java"
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
+local String = require "luan:String.luan"
+local regex = String.regex or error()
+local sub_string = String.sub or error()
+local Http = require "luan:http/Http.luan"
+local Mail = require "luan:mail/Mail.luan"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "Utils"
+
+
+local Utils = {}
+
+Utils.domain = Http.domain or "admin.me.luan.software"
+
+Utils.server_domain = regex([[^admin\.(\w+\Q.luan.software\E)$]]).matches(Utils.domain) or error "invalid domain"
+
+Utils.sites_dir = uri(Http.dir).parent().parent()
+
+local send = Mail.sender{
+	host = "mail.smtp2go.com"
+	port = 465
+	username = "luan.admin2"
+	password = Config.mail_password or error()
+}.send
+
+function Utils.send_mail(mail)
+	mail.From = mail.From or Utils.domain.."<monitor@luan.software>"
+	mail.To = mail.To or Config.email_to or error()
+	send(mail)
+end
+
+function Utils.ssh(host,cmd)
+	local cmd = "ssh -t -oConnectTimeout=10 -oServerAliveInterval=10 -oBatchMode=yes -oStrictHostKeyChecking=no -p 14299 administrator@"..host.." '"..cmd.."'"
+	local con = uri("bash:"..cmd)
+	return con.read_text()
+end
+
+local function last_modified_in_dir(dir)
+	local rtn = 0
+	for _, child in ipairs(dir.children()) do
+		local lm = nil
+		if child.is_directory() then
+			lm = last_modified_in_dir(child)
+		elseif child.is_file() then
+			lm = child.last_modified()
+		end
+		if lm ~= nil and lm > rtn then
+			rtn = lm
+		end
+	end
+	return rtn
+end
+Utils.last_modified_in_dir = last_modified_in_dir
+
+
+local luanhost_logs = uri "site:/private/local/logs/luanhost"
+if not luanhost_logs.exists() then
+	local logs = uri("file:logs").canonical()
+	--logs.mkdir()  -- must exist
+	if logs.exists() then
+		logs.symlink_from(luanhost_logs)
+		logger.info "linked to luanhost logs"
+	else
+		logger.error("logs dir doesn't exist")
+	end
+end
+
+
+return Utils
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/lib/monitor.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,82 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
+local String = require "luan:String.luan"
+local trim = String.trim or error()
+local Time = require "luan:Time.luan"
+local Thread = require "luan:Thread.luan"
+local Http = require "luan:http/Http.luan"
+local Config = require "site:/private/Config.luan"
+local Utils = require "site:/private/lib/Utils.luan"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "monitor"
+
+
+local who_monitors_who = {
+	-- ["admin.me.luan.software"] = "admin.me.luan.software"
+	["admin.s1.luan.software"] = "admin.s2.luan.software"
+	["admin.s2.luan.software"] = "admin.s1.luan.software"
+}
+local frequency = Time.period{minutes=2}
+
+--[[  -- for development
+--who_monitors_who["admin.me.luan.software"] = "admin.me.luan.software"
+frequency = Time.period{seconds=20}
+Config.email_to = "fschmidt@gmail.com"
+--]]
+
+
+
+local domain = who_monitors_who[Utils.domain]
+
+if domain == nil then
+	logger.info "nothing to monitor"
+	return true
+end
+
+local url = "https://"..domain.."/hi"
+local options = { time_out = Time.period{seconds=20} }
+
+local function init_check()
+	local fails = 0
+
+	return function()
+		try
+			local page = uri(url,options).read_text()
+			fails = 0
+			logger.info(domain.." is okay")
+		catch e
+			logger.error("Error connecting to "..domain..": "..e.get_message())
+			fails = fails + 1
+			if fails < 2 then return end
+			try
+				local s = Utils.ssh(domain,"/Users/administrator/luan/host/restart.sh monitoring")
+				if trim(s) == "stopped with stop script" then
+					logger.info("stopped with stop script")
+				else
+					logger.error("restart successful\n"..s)
+					Utils.send_mail {
+						Subject = domain.." restarted"
+						body = s
+					}
+				end
+				fails = 0
+			catch e
+				logger.error("restart failed: "..e.get_message())
+				if fails < 5 then
+					Utils.send_mail {
+						Subject = domain.." restart failed"
+						body = e.get_message()
+					}
+				end
+			end
+		end
+	end
+end
+
+Thread.schedule_closure(init_check,{repeating_delay=frequency})
+
+logger.info("monitoring "..domain)
+
+return true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/admin.html.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,70 @@
+require "site:/init.luan"
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local Io = require "luan:Io.luan"
+local uri = Io.uri or error()
+local String = require "luan:String.luan"
+local Http = require "luan:http/Http.luan"
+require "java"
+local Runtime = require "java:java.lang.Runtime"
+local System = require "java:java.lang.System"
+local Server = require "java:goodjava.webserver.Server"
+
+
+return function()
+	local action = Http.request.parameters.action
+	if action == "gc" then
+		System.gc()
+		Http.response.send_redirect "admin.html"
+		return
+	end
+
+	Io.stdout = Http.response.text_writer()
+
+	local oneMega = 1024 * 1024;
+	local free = Runtime.getRuntime().freeMemory() / oneMega;
+	local total = Runtime.getRuntime().totalMemory() / oneMega;
+	local used = total - free;
+
+	local loadAverage = uri("os:uptime").read_text()
+	--local loadAverage = new String(result).replaceAll(".*average:","");
+
+	local threadPool = Server.threadPool
+	local threads = threadPool.getPoolSize()
+	local active_threads = threadPool.getActiveCount()
+%>
+<!doctype html>
+<html lang="en">
+	<body>
+
+		<table>
+			<tr>
+				<td>Free Memory</td>
+				<td>
+					<%=String.format("%.2f",free)%> Mb
+				</td>
+			</tr>
+			<tr>
+				<td>Used Memory</td>
+				<td><%=String.format("%.2f",used)%> Mb</td>
+			</tr>
+			<tr>
+				<td>Total Memory</td>
+				<td><%=String.format("%.2f",total)%> Mb</td>
+			</tr>
+			<tr>
+				<td>Load Average</td>
+				<td><%=loadAverage%></td>
+			</tr>
+			<tr>
+				<td>Threads</td>
+				<td><%= active_threads %> active, <%= threads - active_threads %> idle</td>
+			</tr>
+		<table>
+
+		<p><a href="admin.html?action=gc">Run GC</a></p>
+
+	</body>
+</html>
+<%
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/clean_logs.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,31 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local String = require "luan:String.luan"
+local regex = String.regex or error()
+local Io = require "luan:Io.luan"
+local Http = require "luan:http/Http.luan"
+local Utils = require "site:/private/lib/Utils.luan"
+
+
+local web_log_regex = regex[[^web.*\.log$]]
+
+return function()
+	Http.response.headers["content-type"] = "text/plain"
+	Io.stdout = Http.response.text_writer()
+	local dirs = Utils.sites_dir.children()
+	for _, site_dir in ipairs(dirs) do
+		local log_dir = site_dir.child("site/private/local/logs")
+		if log_dir.exists() then
+			log_dir.child("luan.log").delete()
+			for _, f in ipairs(log_dir.children()) do
+				if web_log_regex.matches(f.name()) then
+					f.delete()
+				end_if
+			end_for
+		end_if
+	end_for
+%>
+done
+<%
+end_function
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/java_threads.html.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,1 @@
+return require("luan:http/tools/Java_threads.luan").respond
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/links.html	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,32 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta name="viewport" content="width=device-width, initial-scale=1">
+	</head>
+	<body>
+		<h1>Links</h1>
+		<p><a href="https://luanhost.luan.software/">forum</a></p>
+		<p>
+			<a href="https://admin.s1.luan.software/">admin.s1.luan.software</a>
+			- <a href="https://admin.test.luan.software/">admin.test.luan.software</a>
+			- <a href="https://admin.me.luan.software/">admin.me.luan.software</a>
+			- private: admin / VU8-ZbbR!=qUQ5V
+		</p>
+		<p>
+			<a href="https://www.luan.software/">luan</a>
+			- i73tTmyS
+		</p>
+		<p>
+			<a href="https://hg.reactionary.software/repo/luanhost">hg</a>
+			- <a href="https://hg.luan.software/hghost">hghost</a>
+		</p>
+		<p>
+			<a href="https://internetbs.net/">DNS</a>
+			- fschmidt@gmail.com / ibs123
+		</p>
+		<p>
+			<a href="https://www.smtp2go.com/">smtp2go</a>
+			- fschmidt@luan.software / r2r8gxJTS
+		</p>
+	</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/luan_threads.html.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,1 @@
+return require("luan:http/tools/Luan_threads.luan").respond
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/request.txt.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,12 @@
+local Binary = require "luan:Binary.luan"
+local Io = require "luan:Io.luan"
+local Http = require "luan:http/Http.luan"
+
+
+return function()
+	Io.stdout = Http.response.text_writer()
+	%><%= Http.request.raw_head %><%
+	if Http.request.body ~= nil then
+		%><%=Binary.to_string(Http.request.body,"utf-8")%><%
+	end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/run.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,1 @@
+return require("luan:http/tools/Run.luan").respond
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/shell.html.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,1 @@
+return require("luan:http/tools/Shell.luan").respond
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/host/admin/src/private/tools/sites.html.luan	Fri Jul 04 10:25:38 2025 -0600
@@ -0,0 +1,95 @@
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local ipairs = Luan.ipairs or error()
+local Io = require "luan:Io.luan"
+local String = require "luan:String.luan"
+local split = String.split or error()
+local Table = require "luan:Table.luan"
+local concat = Table.concat or error()
+local insert = Table.insert or error()
+local sort = Table.sort or error()
+local Time = require "luan:Time.luan"
+local format_time = Time.format or error()
+local Http = require "luan:http/Http.luan"
+local Utils = require "site:/private/lib/Utils.luan"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "sites.html"
+
+
+local function remove()
+	local site = Http.request.parameters.site or error()
+	local site_dir = Utils.sites_dir.child(site)
+	site_dir.exists() or error()
+	site_dir.delete()
+end
+
+
+return function()
+	local action = Http.request.parameters.action
+	if action == "remove" then
+		remove()
+		Http.response.send_redirect "sites.html"
+		return
+	end
+
+	Io.stdout = Http.response.text_writer()
+	local dirs = Utils.sites_dir.children()
+	for _, dir in ipairs(dirs) do
+		local t = {}
+		for _, s in ipairs{split(dir.name(),".")} do
+			insert(t,1,s)
+		end
+		dir.sort = concat(t,".")
+	end
+	sort(dirs,function(d1,d2)
+		return d1.sort < d2.sort
+	end)
+%>
+<!doctype html>
+<html lang="en">
+	<body>
+		<table>
+			<tr><th></th><th>site</th><th>logs/web</th><th>DNS</th><th></th><th>password</th></tr>
+<%
+	local my_ips = Io.my_ips()
+	for i, site_dir in ipairs(dirs) do
+		local site = site_dir.name()
+		if site_dir.is_directory() then
+			local url = "http://"..site
+			local port = Http.request.port
+			if port ~= nil and port ~= 80 then
+				url = url..":"..port
+			end
+			url = url.."/"
+			local password = Luan.do_file(site_dir.to_string().."/info.luan").password or error()
+			local web_log = site_dir.child("site/private/local/logs/web")
+			local date = web_log.last_modified()
+
+			local dns = ""
+			local ip = Io.ip(site)
+			if my_ips[ip] ~= true then
+				dns = ip or "not found"
+			end
+%>
+			<tr>
+				<td><%=i%></td>
+				<td><a href="<%=url%>"><%=site%></a></td>
+				<td nowrap><%=format_time(date,"yyyy-MM-dd")%></td>
+				<td><%=dns%></td>
+				<td><a href="sites.html?action=remove&site=<%=site%>" onclick="return confirm('Delete <%=site%>?')">remove</a></td>
+				<td><%=password%></td>
+			</tr>
+<%
+		else
+%>
+			<tr><td><%=site%></td></tr>
+<%
+		end
+	end
+%>
+		<table>
+
+	</body>
+</html>
+<%
+end