Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f5a69c86f |
37
.claude/settings.json
Normal file
37
.claude/settings.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"CLAUDE_CODE_SCROLL_SPEED": "3",
|
||||||
|
"ANTHROPIC_MODEL": "sonnet"
|
||||||
|
},
|
||||||
|
"model": "opus[1m]",
|
||||||
|
"effortLevel": "high",
|
||||||
|
"autoUpdatesChannel": "latest",
|
||||||
|
"tui": "fullscreen",
|
||||||
|
"showMessageTimestamps": true,
|
||||||
|
"permissions": {
|
||||||
|
"defaultMode": "auto",
|
||||||
|
"allow": [],
|
||||||
|
"additionalDirectories": []
|
||||||
|
},
|
||||||
|
"enabledPlugins": {
|
||||||
|
"superpowers@claude-plugins-official": true,
|
||||||
|
"context7@claude-plugins-official": true,
|
||||||
|
"core@nicecode": true
|
||||||
|
},
|
||||||
|
"extraKnownMarketplaces": {
|
||||||
|
"nicecode": {
|
||||||
|
"source": {
|
||||||
|
"source": "github",
|
||||||
|
"repo": "lachtan/nicecode"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spinnerVerbs": {
|
||||||
|
"mode": "replace",
|
||||||
|
"verbs": [
|
||||||
|
"Analyzing code",
|
||||||
|
"Crafting solutions",
|
||||||
|
"Working hard"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Caddyfile
Normal file
9
Caddyfile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
frankenphp
|
||||||
|
}
|
||||||
|
|
||||||
|
:80 {
|
||||||
|
root * /app/app/public
|
||||||
|
encode gzip
|
||||||
|
php_server
|
||||||
|
}
|
||||||
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
FROM dunglas/frankenphp:1-php8.3
|
||||||
|
|
||||||
|
RUN install-php-extensions yaml
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY app ./app
|
||||||
|
COPY conf ./conf
|
||||||
|
COPY manifest.yaml ./manifest.yaml
|
||||||
|
COPY Caddyfile /etc/frankenphp/Caddyfile
|
||||||
12
Makefile
Normal file
12
Makefile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
COMPOSE ?= docker compose
|
||||||
|
|
||||||
|
.PHONY: build up down
|
||||||
|
|
||||||
|
build:
|
||||||
|
$(COMPOSE) build
|
||||||
|
|
||||||
|
up:
|
||||||
|
$(COMPOSE) up -d
|
||||||
|
|
||||||
|
down:
|
||||||
|
$(COMPOSE) down
|
||||||
23
app/public/index.php
Normal file
23
app/public/index.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require __DIR__ . '/../src/functions.php';
|
||||||
|
|
||||||
|
define('PUBLIC_BASE_URL', resolvePublicBaseUrl());
|
||||||
|
|
||||||
|
$name = resolveRequestedName();
|
||||||
|
|
||||||
|
if ($name === '') {
|
||||||
|
handleIndexRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($name === 'list') {
|
||||||
|
handleListRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($name, 'raw/')) {
|
||||||
|
handleRawRequest(substr($name, strlen('raw/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInstallRequest($name);
|
||||||
175
app/src/functions.php
Normal file
175
app/src/functions.php
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
const MANIFEST_PATH = 'manifest.yaml';
|
||||||
|
const REPO_ROOT_DIRECTORY = __DIR__ . '/../..';
|
||||||
|
const TEMPLATES_DIRECTORY = __DIR__ . '/../templates';
|
||||||
|
|
||||||
|
function sendPlainTextError(int $statusCode, string $message): never
|
||||||
|
{
|
||||||
|
http_response_code($statusCode);
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
echo $message . "\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeForDoubleQuotedShellString(string $value): string
|
||||||
|
{
|
||||||
|
return str_replace(['\\', '"', '$', '`'], ['\\\\', '\\"', '\\$', '\\`'], $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveHomePath(string $path): string
|
||||||
|
{
|
||||||
|
if ($path === '~' || str_starts_with($path, '~/')) {
|
||||||
|
return '$HOME' . escapeForDoubleQuotedShellString(substr($path, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return escapeForDoubleQuotedShellString($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplate(string $path, array $placeholders): string|false
|
||||||
|
{
|
||||||
|
if (!is_file($path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents($path);
|
||||||
|
|
||||||
|
foreach ($placeholders as $key => $value) {
|
||||||
|
$content = str_replace('{{' . $key . '}}', $value, $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readRepoFile(string $relativePath): string|false
|
||||||
|
{
|
||||||
|
$path = REPO_ROOT_DIRECTORY . '/' . ltrim($relativePath, '/');
|
||||||
|
|
||||||
|
if (!is_file($path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents($path);
|
||||||
|
|
||||||
|
return is_string($content) ? $content : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadManifest(): array
|
||||||
|
{
|
||||||
|
$manifestYaml = readRepoFile(MANIFEST_PATH);
|
||||||
|
|
||||||
|
if ($manifestYaml === false) {
|
||||||
|
sendPlainTextError(500, 'Failed to read the manifest');
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest = yaml_parse($manifestYaml);
|
||||||
|
|
||||||
|
if ($manifest === false) {
|
||||||
|
sendPlainTextError(500, 'Failed to parse the manifest');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findManifestEntry(string $name): array
|
||||||
|
{
|
||||||
|
$manifest = loadManifest();
|
||||||
|
|
||||||
|
$entry = $manifest[$name] ?? null;
|
||||||
|
|
||||||
|
if ($entry === null) {
|
||||||
|
sendPlainTextError(404, "Unknown item '$name'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_string($entry['source'] ?? null) || !is_string($entry['target'] ?? null) || !is_string($entry['template'] ?? null)) {
|
||||||
|
sendPlainTextError(500, "Malformed manifest entry for item '$name'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePublicBaseUrl(): string
|
||||||
|
{
|
||||||
|
$publicBaseUrl = getenv('PUBLIC_BASE_URL');
|
||||||
|
|
||||||
|
if ($publicBaseUrl === false || $publicBaseUrl === '') {
|
||||||
|
sendPlainTextError(500, 'PUBLIC_BASE_URL is not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $publicBaseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRequestedName(): string
|
||||||
|
{
|
||||||
|
$requestPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?? '/';
|
||||||
|
|
||||||
|
return trim(rawurldecode($requestPath), '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleIndexRequest(): never
|
||||||
|
{
|
||||||
|
$text = renderTemplate(TEMPLATES_DIRECTORY . '/index.tpl', [
|
||||||
|
'PUBLIC_BASE_URL' => rtrim(PUBLIC_BASE_URL, '/'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($text === false) {
|
||||||
|
sendPlainTextError(500, 'Index template is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
echo $text;
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleListRequest(): never
|
||||||
|
{
|
||||||
|
$names = array_keys(loadManifest());
|
||||||
|
sort($names);
|
||||||
|
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
echo "Available configurations:\n";
|
||||||
|
|
||||||
|
foreach ($names as $name) {
|
||||||
|
echo " $name\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRawRequest(string $name): never
|
||||||
|
{
|
||||||
|
$entry = findManifestEntry($name);
|
||||||
|
|
||||||
|
$content = readRepoFile($entry['source']);
|
||||||
|
|
||||||
|
if ($content === false) {
|
||||||
|
sendPlainTextError(500, "Failed to read the file for item '$name'");
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/octet-stream');
|
||||||
|
echo $content;
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInstallRequest(string $name): never
|
||||||
|
{
|
||||||
|
$entry = findManifestEntry($name);
|
||||||
|
|
||||||
|
$variables = [
|
||||||
|
'SOURCE_URL' => rtrim(PUBLIC_BASE_URL, '/') . '/raw/' . rawurlencode($name),
|
||||||
|
'TARGET_PATH' => resolveHomePath($entry['target']),
|
||||||
|
'POST_INSTALL' => $entry['post_install'] ?? '',
|
||||||
|
];
|
||||||
|
|
||||||
|
$script = renderTemplate(TEMPLATES_DIRECTORY . '/' . $entry['template'] . '.sh.tpl', $variables);
|
||||||
|
|
||||||
|
if ($script === false) {
|
||||||
|
sendPlainTextError(500, 'Template for this item is missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: text/x-shellscript; charset=utf-8');
|
||||||
|
echo $script;
|
||||||
|
exit;
|
||||||
|
}
|
||||||
11
app/templates/config.sh.tpl
Normal file
11
app/templates/config.sh.tpl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
readonly SOURCE_URL="{{SOURCE_URL}}"
|
||||||
|
readonly TARGET_PATH="{{TARGET_PATH}}"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$TARGET_PATH")"
|
||||||
|
curl -fsSL "$SOURCE_URL" -o "$TARGET_PATH"
|
||||||
|
echo "Configuration installed: $TARGET_PATH"
|
||||||
|
{{POST_INSTALL}}
|
||||||
13
app/templates/index.tpl
Normal file
13
app/templates/index.tpl
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Fast access to configurations and installation scripts.
|
||||||
|
|
||||||
|
Install a configuration:
|
||||||
|
|
||||||
|
curl -fsSL {{PUBLIC_BASE_URL}}/<name> | bash
|
||||||
|
|
||||||
|
List all available configurations:
|
||||||
|
|
||||||
|
curl -fsSL {{PUBLIC_BASE_URL}}/list
|
||||||
|
|
||||||
|
Download the raw source file instead of the install script:
|
||||||
|
|
||||||
|
curl -fsSL {{PUBLIC_BASE_URL}}/raw/<name>
|
||||||
8
compose.yaml
Normal file
8
compose.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
services:
|
||||||
|
get:
|
||||||
|
build: .
|
||||||
|
environment:
|
||||||
|
PUBLIC_BASE_URL: https://get.fnet.cz/
|
||||||
|
ports:
|
||||||
|
- "8082:80"
|
||||||
|
restart: unless-stopped
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
# tmux config - screen-like key bindings
|
# tmux config - screen-like key bindings
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
||||||
# mkdir -p $HOME/.config/tmux
|
# mkdir -p $HOME/.config/tmux && curl https://git.fnet.cz/lachtan/get/raw/branch/master/conf/tmux.conf -o $HOME/.config/tmux/tmux.conf
|
||||||
# curl https://git.fnet.cz/lachtan/get/raw/branch/master/conf/tmux.conf -o $HOME/.config/tmux/tmux.conf
|
|
||||||
|
|
||||||
# Searched in this oreder:
|
# Searched in this oreder:
|
||||||
# /etc/tmux.conf
|
# /etc/tmux.conf
|
||||||
|
|||||||
4
manifest.yaml
Normal file
4
manifest.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
tmux:
|
||||||
|
template: config
|
||||||
|
source: conf/tmux.conf
|
||||||
|
target: ~/.config/tmux/tmux.conf
|
||||||
Reference in New Issue
Block a user