# Simple url shortener backend This is only intended for personal use, possibly giving urls to a few friends. Nothing that requires scaling. This is made by Gemini using the following prompts. The frontend then sends JSON to the backend of the form ```json { "short": "never", "url": "https://gonna.give.you.up/" } ``` To delete, set the url to `!delete`. Then one can write a simple frontend with Javascript. CORS headers are sent in the `cors.php` file if needed (e.g. if the add short interface is on a different domain than the get short frontend, nice for security as the main user-facing frontend domain has no capacity to add or delete). ```php url", "short": short, "url": url }, or { "message": "deleted abc" } or { "error": "error message" . So a success is indicated by the presence of a "message" element, or else there is an "error" element. If there is a PHP error of some sort, the client should write this to the console. The top of the PHP backend has an easy way to enable/disable detailed error printing in the case of PHP errors. The backend database is Mysql. ``` # 2 Prompt for Gemini ```g The dbauth.php defines define('HOST',"..."); define('PORT',...); define('USER',"..."); define('PASS',"..."); define('DB',"..."); ``` ## Example reporting errors (from Gemini) ```js fetch('api/index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ short: 'nas', url: 'http://nas-server/storage' }) }) .then(response => { if (!response.ok) { // This catches 500 errors and logs the PHP output to console response.text().then(text => console.error("PHP Error:", text)); } return response.json(); }) .then(data => { if (data.message) console.log("Success:", data.message); if (data.error) console.warn("App Error:", data.error); }); ``` # table create (from Gemini) ```g CREATE TABLE IF NOT EXISTS `YOURPREFIX_short_to_url` ( `short` VARCHAR(191) NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY (`short`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` # table create php (from Gemini) ```php connect_error) { throw new Exception("Connection failed: " . $mysqli->connect_error); } // 2. Prepare the Table Name $tableName = PREFIX . "_short_to_url"; // 3. Define the SQL // We use VARCHAR(191) for 'short' to ensure compatibility with utf8mb4 indexing $sql = "CREATE TABLE IF NOT EXISTS `$tableName` ( `short` VARCHAR(191) NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY (`short`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; // 4. Execute if ($mysqli->query($sql) === TRUE) { echo "

Success!

"; echo "Table $tableName is ready."; } else { throw new Exception("Error creating table: " . $mysqli->error); } $mysqli->close(); } catch (Exception $e) { echo "

Setup Failed

"; echo "Error: " . $e->getMessage(); } ``` # PHP Backend ```php db = new mysqli(HOST, USER, PASS, DB, PORT); if ($this->db->connect_error) { throw new Exception("Connection failed: " . $this->db->connect_error); } // table name: PREFIX_short_to_url $this->table = PREFIX . "_short_to_url"; } public function handleRequest($jsonInput) { $data = json_decode($jsonInput, true); // Validation: Basic structure if (!$data || !isset($data['short']) || !isset($data['url'])) { return ["error" => "Invalid JSON: 'short' and 'url' fields required."]; } $short = trim($data['short']); $url = trim($data['url']); if (empty($short)) { return ["error" => "The 'short' value cannot be empty."]; } // Action: Delete if ($url === "!delete") { return $this->deleteShort($short); } // Action: Add/Update // Validates protocol; accepts LAN domains (no period required) if (preg_match('~^(?:f|ht)tps?://~i', $url)) { return $this->upsertShort($short, $url); } return ["error" => "Invalid URL format or command."]; } private function upsertShort($short, $url) { $sql = "INSERT INTO {$this->table} (short, url) VALUES (?, ?) ON DUPLICATE KEY UPDATE url = VALUES(url)"; $stmt = $this->db->prepare($sql); $stmt->bind_param("ss", $short, $url); if ($stmt->execute()) { return [ "message" => "added $short => $url", "short" => $short, "url" => $url ]; } return ["error" => "DB Execution failed: " . $this->db->error]; } private function deleteShort($short) { $stmt = $this->db->prepare("DELETE FROM {$this->table} WHERE short = ?"); $stmt->bind_param("s", $short); if ($stmt->execute()) { return ["message" => "deleted $short"]; } return ["error" => "DB Execution failed: " . $this->db->error]; } } /** * Execution */ try { $manager = new UrlManager(); $rawInput = file_get_contents('php://input'); $response = $manager->handleRequest($rawInput); echo json_encode($response); } catch (Exception $e) { // If PHP crashes, this is caught here. // Client logs response text to console. http_response_code(500); echo json_encode(["error" => $e->getMessage()]); } ``` `dbauth.php` defines the constants for the database `HOST`, `USER`, `PASS`, `DB`, `PORT`. `bouncer.php` is arbitrary PHP code that determines if the request is authorised, and errors out if not. # `add_short_common.php` - Web frontend to set shorts ```php URL Shortener

Add a Short URL

> >
``` # Redirector ```php connect_error) { die("Connection failed: " . $conn->connect_error); } $stmt = $conn->prepare("SELECT id, short, url FROM ".PREFIX."_short_to_url WHERE short = ?"); $stmt->bind_param("s", $req); $stmt->execute(); $result = $stmt->get_result(); if ($row = $result->fetch_assoc()) { $url = $row["url"]; $url = str_replace("%s",$qstring,$url); } else { $url = null; } $stmt->close(); $conn->close();