examples: DEVfromGround vc_org, NODEJS, VM, POSTGRES updates

This commit is contained in:
Naeel
2026-03-26 08:33:03 +03:00
parent 2b03ba520e
commit 547994dc55
11 changed files with 216 additions and 217 deletions
+83 -98
View File
@@ -1,120 +1,105 @@
# Создано: 2026-04-10
# Простой калькулятор — возвращает HTML страницу с кнопками.
# Роутинг: GET → HTML страница; POST с {a, op, b} → вычисление и редирект с результатом.
# _method приходит от python3.11 рантайма в event.
# Изменено: 2026-03-23 — упрощён до поля ввода выражения (демонстрация деплоя).
# Принимает произвольное математическое выражение: "2+2*(3-1)", "(10/3)**2" и т.д.
# GET → HTML страница с формой; POST с {expr} → вычисление через безопасный eval.
# Безопасность eval: __builtins__=None, только math-функции в locals.
_HTML = """<!DOCTYPE html>
import math
_PAGE = """<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Калькулятор (Python)</title>
<title>Калькулятор Python 3.11</title>
<style>
body { font-family: sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #1a1a2e; }
.calc { background: #16213e; border-radius: 16px; padding: 24px; box-shadow: 0 8px 32px rgba(0,0,0,0.5); width: 280px; }
h2 { color: #e2e8f0; text-align: center; margin: 0 0 16px; font-size: 18px; }
.badge { text-align: center; font-size: 11px; color: #64748b; margin-bottom: 16px; }
.display { background: #0f172a; color: #f1f5f9; font-size: 28px; text-align: right; padding: 12px 16px; border-radius: 8px; margin-bottom: 16px; min-height: 52px; word-break: break-all; }
.result { color: #22d3ee; font-size: 20px; text-align: right; padding: 4px 16px; margin-bottom: 8px; min-height: 28px; }
.btns { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
button { border: none; border-radius: 8px; padding: 16px 0; font-size: 18px; cursor: pointer; transition: filter 0.1s; }
button:active { filter: brightness(1.3); }
.btn-num { background: #334155; color: #f1f5f9; }
.btn-op { background: #0891b2; color: #fff; }
.btn-eq { background: #0d9488; color: #fff; grid-column: span 2; }
.btn-clr { background: #7f1d1d; color: #fca5a5; grid-column: span 2; }
.btn-zero { grid-column: span 2; }
body { font-family: monospace; background: #0f172a; color: #e2e8f0;
display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; }
.box { background: #1e293b; border-radius: 12px; padding: 32px; width: 420px; box-shadow: 0 8px 32px #0005; }
h2 { margin: 0 0 4px; font-size: 20px; color: #7dd3fc; }
.sub { color: #475569; font-size: 12px; margin-bottom: 24px; }
input { width: 100%; box-sizing: border-box; padding: 10px 14px; font-size: 18px; font-family: monospace;
background: #0f172a; border: 1px solid #334155; border-radius: 8px; color: #f1f5f9; outline: none; }
input:focus { border-color: #38bdf8; }
button { margin-top: 12px; width: 100%; padding: 12px; font-size: 16px; background: #0369a1;
color: #fff; border: none; border-radius: 8px; cursor: pointer; }
button:hover { background: #0284c7; }
button:disabled { background: #1e3a5f; color: #475569; cursor: default; }
.result { margin-top: 20px; padding: 14px; border-radius: 8px; font-size: 22px; text-align: center; display: none; }
.ok { background: #064e3b; color: #6ee7b7; display: block; }
.err { background: #450a0a; color: #fca5a5; font-size: 14px; display: block; }
</style>
</head>
<body>
<div class="calc">
<div class="box">
<h2>Калькулятор</h2>
<div class="badge">Python 3.11</div>
<div id="display" class="display">0</div>
<div id="result" class="result">{result_line}</div>
<div class="btns">
<button class="btn-clr btn-zero" onclick="clr()">C</button>
<button class="btn-op" onclick="setOp('%2F')">÷</button>
<button class="btn-op" onclick="setOp('*')">×</button>
<button class="btn-num" onclick="inp('7')">7</button>
<button class="btn-num" onclick="inp('8')">8</button>
<button class="btn-num" onclick="inp('9')">9</button>
<button class="btn-op" onclick="setOp('-')"></button>
<button class="btn-num" onclick="inp('4')">4</button>
<button class="btn-num" onclick="inp('5')">5</button>
<button class="btn-num" onclick="inp('6')">6</button>
<button class="btn-op" onclick="setOp('+')">+</button>
<button class="btn-num" onclick="inp('1')">1</button>
<button class="btn-num" onclick="inp('2')">2</button>
<button class="btn-num" onclick="inp('3')">3</button>
<button class="btn-num btn-zero" onclick="inp('0')">0</button>
<button class="btn-num" onclick="inp('.')">.</button>
<button class="btn-eq" onclick="calc()">=</button>
</div>
<div class="sub">Python 3.11 · runtime: sless</div>
<input id="expr" autofocus placeholder="например: 2 + 2 * (3 - 1)">
<button id="btn" onclick="calc()">Вычислить</button>
<div id="result" class="result"></div>
</div>
<script>
let a = '', op = '', b = '', fresh = {fresh_js};
const disp = document.getElementById('display');
function inp(v) {{
if (op === '') {{ a += v; disp.textContent = a || '0'; }}
else {{ b += v; disp.textContent = b || '0'; }}
fresh = false;
}}
function setOp(o) {{
if (a === '') return;
if (b !== '') calc();
else {{ op = o; disp.textContent = a + ' ' + decodeURIComponent(o) + ''; }}
fresh = false;
}}
function clr() {{ a = ''; op = ''; b = ''; disp.textContent = '0'; }}
function calc() {{
if (a === '' || op === '' || b === '') return;
const form = document.createElement('form');
form.method = 'POST'; form.action = '';
const fields = {{a, op, b}};
for (const [k, v] of Object.entries(fields)) {{
const i = document.createElement('input');
i.type = 'hidden'; i.name = k; i.value = decodeURIComponent(v);
form.appendChild(i);
}}
document.body.appendChild(form);
form.submit();
}}
document.getElementById('expr').addEventListener('keydown', function(e) {
if (e.key === 'Enter') calc();
});
async function calc() {
const expr = document.getElementById('expr').value.trim();
if (!expr) return;
const btn = document.getElementById('btn');
const res = document.getElementById('result');
btn.disabled = true;
btn.textContent = '';
try {
const r = await fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({expr: expr})
});
const data = await r.json();
if (data.error) {
res.className = 'result err';
res.textContent = data.error;
} else {
res.className = 'result ok';
res.textContent = expr + ' = ' + data.result;
}
} catch(e) {
res.className = 'result err';
res.textContent = 'Ошибка сети: ' + e.message;
}
btn.disabled = false;
btn.textContent = 'Вычислить';
}
</script>
</body>
</html>"""
# Разрешённые math-функции в eval — без __builtins__ нет доступа к exec/open/etc.
_MATH_LOCALS = {k: getattr(math, k) for k in dir(math) if not k.startswith('_')}
def handler(event):
# POST: тело приходит как JSON от рантайма
if event.get('_method') == 'POST':
return _compute(event)
# GET: показываем чистую страницу
return _html()
expr = str(event.get('expr', '')).strip()
return _compute(expr)
# GET → HTML страница
return _PAGE
def _html(result_line='', fresh_js='true'):
return _HTML.replace('{result_line}', result_line).replace('{fresh_js}', fresh_js)
def _compute(event):
def _compute(expr):
if not expr:
return {'error': 'Введите выражение'}
try:
a = float(event.get('a', 0))
b = float(event.get('b', 0))
op = event.get('op', '')
if op == '+':
res = a + b
elif op == '-':
res = a - b
elif op == '*':
res = a * b
elif op == '/':
if b == 0:
return _html('Деление на ноль', 'false')
res = a / b
else:
return _html('Неизвестный оператор', 'false')
# Убираем лишний .0 для целых результатов
res_str = str(int(res)) if res == int(res) else str(res)
return _html(f'{int(a) if a == int(a) else a} {op} {int(b) if b == int(b) else b} = {res_str}', 'false')
except (ValueError, TypeError) as exc:
return _html(f'Ошибка: {exc}', 'false')
result = eval(expr, {'__builtins__': None}, _MATH_LOCALS) # noqa: S307
if not isinstance(result, (int, float)):
return {'error': 'Результат не является числом'}
return {'expr': expr, 'result': result}
except ZeroDivisionError:
return {'error': 'Деление на ноль'}
except Exception as exc:
return {'error': f'Ошибка: {exc}'}
def _esc(s):
# Экранируем HTML-спецсимволы — безопасный вывод в атрибут и тело.
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')