Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
473623a
Merge pull request #866 from LinkStackOrg/beta
JulianPrieber Dec 10, 2024
e681cd7
Update version.json to 4.8.4
JulianPrieber Dec 10, 2024
51917e7
Update README.md
lastsamurai26 Feb 6, 2025
6489a95
Update README.md
JulianPrieber Jun 3, 2025
bd5bafd
update zh_TW translation
LokiSalmonNeko Nov 17, 2025
4e3c323
update translation files
LokiSalmonNeko Nov 25, 2025
3678fa0
Update image links in README.md
JulianPrieber Dec 3, 2025
93c6a63
Cache user image path
JulianPrieber Dec 16, 2025
604653f
Update Dependencies (#958)
JulianPrieber Jan 26, 2026
0204a15
Update Ubuntu
JulianPrieber Jan 26, 2026
67e5146
Test fix PHP 8
JulianPrieber Jan 26, 2026
0673282
Revert "Test fix PHP 8"
JulianPrieber Jan 26, 2026
7ad4a65
Install extensions manually
JulianPrieber Jan 26, 2026
b7c7881
Rever to ubuntu-20.04
JulianPrieber Jan 26, 2026
bfac213
Test shivammathur/setup-php
JulianPrieber Jan 26, 2026
f4649ea
Remove discontinued dependency
JulianPrieber Jan 26, 2026
dca586e
Update version.json to 4.8.5
JulianPrieber Jan 26, 2026
f8734c1
Fix dependencies
JulianPrieber Feb 17, 2026
46ba4d0
Update version.json to 4.8.6
JulianPrieber Feb 17, 2026
c42bbed
Merge branch 'main' of https://github.com/LinkStackOrg/LinkStack
JulianPrieber Feb 17, 2026
e2631c0
Fix main beta version missmatch
JulianPrieber Feb 17, 2026
965ddb7
Update composer.json
JulianPrieber Feb 17, 2026
2dfa544
Update version.json to 4.8.6
JulianPrieber Feb 17, 2026
03b122d
Merge branch 'LinkStackOrg:main' into main
LokiSalmonNeko Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

jobs:
setup-ubuntu:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04

steps:
- name: Check for GitHub API key
Expand All @@ -21,14 +21,21 @@ jobs:
echo "GitHub API key found."
fi

- name: Setup PHP 8.0 and Composer
uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
tools: composer:v2
extensions: mbstring, intl, zip, curl, sqlite, pdo_sqlite
coverage: none

- name: Verify PHP version
run: php -v

- name: Updating Dependencies + zip
run: |
cd ~
curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
HASH=`curl -sS https://composer.github.io/installer.sig`
sudo php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer

sudo update-alternatives --set php /usr/bin/php8.0
# Composer already provided by setup-php

cd /tmp
mkdir linkstack
Expand All @@ -52,9 +59,7 @@ jobs:

cp "../../version.json" "version.json"

composer update --no-scripts

php artisan lang:update
composer update --no-dev --no-scripts

php artisan migrate
php artisan db:seed
Expand Down Expand Up @@ -173,12 +178,12 @@ jobs:
TAG_VERSION="${GITHUB_REF##*/}"
version=${TAG_VERSION#"v"}

# Install the OpenSSH client
sudo apt-get install -y openssh-client
# Install required clients
sudo apt-get update
sudo apt-get install -y openssh-client sshpass

# Clear the remote directory
sshpass -p "${{ secrets.SERVER_PASSWORD }}" ssh -o StrictHostKeyChecking=no -p ${{ secrets.SERVER_PORT }} ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }} "rm -rf ${{ secrets.REMOTE_PATH }}/*"

# Use SSH to upload the file to the remote server
sshpass -p "${{ secrets.SERVER_PASSWORD }}" scp -o StrictHostKeyChecking=no -P ${{ secrets.SERVER_PORT }} $version.zip ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }}:${{ secrets.REMOTE_PATH }}

sshpass -p "${{ secrets.SERVER_PASSWORD }}" scp -o StrictHostKeyChecking=no -P ${{ secrets.SERVER_PORT }} $version.zip ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }}:${{ secrets.REMOTE_PATH }}
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@
</p>

<p align="center">
<a href="https://github.com/linkstackorg/linkstack/stargazers"><img alt="GitHub Repo stars" src="https://img.lss.ovh/github/stars/julianprieber/littlelink-custom?label=Star%20the%20project&logo=GitHub"></a>
<a href="https://mstdn.social/@linkstack"><img alt="Mastodon Follow" src="https://img.lss.ovh/mastodon/follow/110147874401985724?domain=http%3A%2F%2Fmstdn.social&style=social"></a>
<a href="https://discord.linkstack.org"><img alt="Discord online user count" src="https://img.lss.ovh/discord/955765706111193118?color=4A55CC&label=Discord&logo=Discord&style=flat"></a>
<a href="https://github.com/linkstackorg/linkstack/stargazers"><img alt="GitHub Repo stars" src="https://img.tny.st/github/stars/julianprieber/littlelink-custom?label=Star%20the%20project&logo=GitHub"></a>
<a href="https://mstdn.social/@linkstack"><img alt="Mastodon Follow" src="https://img.tny.st/mastodon/follow/110147874401985724?domain=http%3A%2F%2Fmstdn.social&style=social"></a>
<a href="https://discord.linkstack.org"><img alt="Discord online user count" src="https://img.tny.st/discord/955765706111193118?color=4A55CC&label=Discord&logo=Discord&style=flat"></a>
</p>
<p align="center">
<a href="https://github.com/sponsors/julianprieber"><img alt="GitHub spomsors" src="https://img.lss.ovh/github/sponsors/JulianPrieber?color=BF4B8A&logo=githubsponsors&style=flat&label=Sponsor%20us%20on%20Github"></a>
<a href="https://patreon.com/julianprieber"><img alt="Patreon" src="https://img.lss.ovh/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Djulianprieber%26type%3Dpatrons&style=flat&logo=patreon"></a>
<a href="https://liberapay.com/LittleLink-Custom"><img src="https://img.lss.ovh/liberapay/patrons/LittleLink-Custom?logo=liberapay&label=LiberaPay patrons"></a>
<a href="https://github.com/sponsors/julianprieber"><img alt="GitHub spomsors" src="https://img.tny.st/github/sponsors/JulianPrieber?color=BF4B8A&logo=githubsponsors&style=flat&label=Sponsor%20us%20on%20Github"></a>
<a href="https://patreon.com/julianprieber"><img alt="Patreon" src="https://img.tny.st/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Djulianprieber%26type%3Dpatrons&style=flat&logo=patreon"></a>
<a href="https://liberapay.com/LinkStack"><img src="https://img.tny.st/liberapay/patrons/LinkStack?logo=liberapay&label=LiberaPay%20patrons"></a>
</p>

---

<p align="center">
<a href="https://github.com/linkstackorg/linkstack/releases/latest/download/linkstack.zip"><img src="https://raw.githubusercontent.com/LinkStackOrg/branding/main/badges/png/download_latest.png" alt="Download latest" width="380" ></a>
<br>
<a href="https://github.com/linkstackorg/linkstack/releases"><img alt="GitHub release (latest by date)" src="https://img.lss.ovh/github/v/release/JulianPrieber/LittleLink-Custom?label=Latest%20release"></a>
<a href="https://github.com/linkstackorg/linkstack/releases"><img alt="GitHub release (latest by date)" src="https://img.tny.st/github/v/release/JulianPrieber/LittleLink-Custom?label=Latest%20release"></a>
</p>

---
Expand Down Expand Up @@ -169,7 +169,7 @@ The official docker version of [LinkStack](https://github.com/linkstackorg/links

The docker version of LinkStack retains all the features and customization options of the [original version](https://github.com/linkstackorg/linkstack).

This docker is based on [Alpine Linux](https://www.alpinelinux.org), a Linux distribution designed to be small, simple and secure. The web server is running [Apache2](https://www.apache.org), a free and open-source cross-platform web server software. The docker comes with [PHP 8.0](https://www.php.net/releases/8.0/en.php) for high compatibility and performance.
This docker is based on [Alpine Linux](https://www.alpinelinux.org), a Linux distribution designed to be small, simple and secure. The web server is running [Apache2](https://www.apache.org), a free and open-source cross-platform web server software. The docker comes with [PHP 8.2](https://www.php.net/releases/8.2/en.php) for high compatibility and performance.

#### Using the docker is as simple as pulling and deploying.

Expand Down Expand Up @@ -221,7 +221,7 @@ The updater may fail without throwing an error and just remain on the current ve
<a name="License"></a>
## License

[![License: AGPL v3](https://img.lss.ovh/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![License: AGPL v3](https://img.tny.st/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)

As of version 4.0.0, the license for this project has been updated to the GNU Affero General Public License v3.0, which explicitly requires that any modifications made to the project must be made public. This license also requires that a copyright notice and license notice be included in any copies or derivative works of the project.

Expand Down
92 changes: 77 additions & 15 deletions app/Functions/functions.php
Original file line number Diff line number Diff line change
@@ -1,48 +1,110 @@
<?php

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;

if (!function_exists('preloadDirectoryFiles')) {
/**
* Preload all files in a directory and optionally cache in Redis or Laravel cache.
*
* @param string $directory
* @param string $cacheKey Unique cache key
* @param int $ttl Cache time in seconds
* @return array Array of filenames
*/
function preloadDirectoryFiles(string $directory, string $cacheKey, int $ttl = 3600): array
{
static $memoryCache = [];

// Return from memory if already loaded
if (isset($memoryCache[$directory])) {
return $memoryCache[$directory];
}

$files = [];
$fingerprint = md5(json_encode(scandir($directory)));

// Try Redis first
if (class_exists('Redis')) {
try {
$cached = Redis::get($cacheKey);
if ($cached) {
$cached = json_decode($cached, true);
if (isset($cached['fingerprint']) && $cached['fingerprint'] === $fingerprint) {
$memoryCache[$directory] = $cached['files'];
return $cached['files'];
}
}
} catch (\Exception $e) {
// Redis not available, fallback to local memory
}
}

// Scan directory and cache
$files = scandir($directory);

// Cache in Redis if possible
$data = [
'fingerprint' => $fingerprint,
'files' => $files
];
if (class_exists('Redis')) {
try {
Redis::setex($cacheKey, $ttl, json_encode($data));
} catch (\Exception $e) {
// fallback silently
}
} else {
// Laravel cache fallback
Cache::put($cacheKey, $data, $ttl);
}

$memoryCache[$directory] = $files;

return $files;
}
}

function findFile($name)
{
$directory = base_path("/assets/linkstack/images/");
$files = scandir($directory);
$pathinfo = "error.error";
$files = preloadDirectoryFiles($directory, 'linkstack_images_files');

$pattern = '/^' . preg_quote($name, '/') . '(_\w+)?\.\w+$/i';
foreach ($files as $file) {
if (preg_match($pattern, $file)) {
$pathinfo = $file;
break;
return $file;
}
}
return $pathinfo;
return "error.error";
}

function findAvatar($name)
{
$directory = base_path("assets/img");
$files = scandir($directory);
$pathinfo = "error.error";
$files = preloadDirectoryFiles($directory, 'assets_img_files');

$pattern = '/^' . preg_quote($name, '/') . '(_\w+)?\.\w+$/i';
foreach ($files as $file) {
if (preg_match($pattern, $file)) {
$pathinfo = "assets/img/" . $file;
break;
return "assets/img/" . $file;
}
}
return $pathinfo;
return "error.error";
}

function findBackground($name)
{
$directory = base_path("assets/img/background-img/");
$files = scandir($directory);
$pathinfo = "error.error";
$files = preloadDirectoryFiles($directory, 'assets_img_background_files');

$pattern = '/^' . preg_quote($name, '/') . '(_\w+)?\.\w+$/i';
foreach ($files as $file) {
if (preg_match($pattern, $file)) {
$pathinfo = $file;
break;
return $file;
}
}
return $pathinfo;
return "error.error";
}

function analyzeImageBrightness($file) {
Expand Down
Loading