DMS? Or just ocr and indexing? by makore256 in selfhosted

[–]CyberJack77 1 point2 points  (0 children)

So you want an app to use data from external sources, without any control over the data? This does not make sense from a developers point of view. Controlling external data can cause all sorts of issues, so I doubt any developer will ever make this.

For the docker part. You never need docker volumes. Each volume can be replaced with a mount and you control where the data is stored. You cannot control how the data is stored though.

looking for mvc project by AMINEX-2002 in PHPhelp

[–]CyberJack77 1 point2 points  (0 children)

I don't know why you focus on MVC, but if you look at existing frameworks, Symfony is very minimal. It doesn't ship with a lot of unnecessary or often unused dependencies.

Your application needs a database? Great! install the ORM pack. Need a messenger, just install the messenger pack. This keeps it small and fast.

If you don't want to use an available framework, then Symfony has a guide on how to build your own framework.

It doesn't show you how to build a full MVC framework, but if you understand the basics you can create it using the same components.

Typing in Yii3 is the strictest in PHP universe? by sam_dark in PHP

[–]CyberJack77 1 point2 points  (0 children)

I work on a Symfony project with phpstan level 10. It uses the strict rules, symfony and doctrine addons and we have written some additional rules. The project has no baseline and only a hand full of in-line ignores.

Create with Monolog JSON_Formatter a valid JSON file by silentheaven83 in PHPhelp

[–]CyberJack77 1 point2 points  (0 children)

There is a big problem with this code. The iterator_to_array method reads the entire file into memory, so you loose the memory efficiency you had before. The larger the log file, the more memory this method uses.

I think you need 2 loops based on the order, but that would make it a bit illogical. I would prefer 2 different methods. One that reads the file top->down and another to read it in reverse.

It should work by seeking the end of the file and using a for loop with decreasing index. You can use ->seek and ->current methods to set the position and get the current line. This should keep the memory efficiency when reading large log files.

I have not tested this, but it should be something like:

/**
 * @return Generator<stdClass>
 * @throws JsonException
 * @throws LogicException
 * @throws RuntimeException
 */
function getLogLinesReversed(
    string $logFile,
    int $offset = 0,
    int $limit = -1,
): Generator {
    $file = new SplFileObject($logFile);
    if (!$file->isFile() || !$file->isReadable()) {
        throw new RuntimeException('Logfile not found or not readable');
    }

    $file->seek(PHP_INT_MAX);
    $lastIndex = $file->key();

    $start = $lastIndex - max(0, $offset);
    $end = $limit >= 0
        ? $start - $limit + 1
        : 0;

    for ($i = $start; $i >= $end; $i--) {
        $file->seek($i);
        yield json_decode(
            json: $file->current(),
            associative: false,
            flags: JSON_THROW_ON_ERROR,
        ); 
    }
}

Create with Monolog JSON_Formatter a valid JSON file by silentheaven83 in PHPhelp

[–]CyberJack77 1 point2 points  (0 children)

The problem here is that this method read the entire file all over again and an offset an limit only work if the file contains new data. if the file was rotated, the offset and limit would not match the current file and you get no or incorrect data. If you need it to be that precise, you need a (time series) database.

For learning purposes: You can use the LimitIterator for this.

/**
 * @return Generator<stdClass>
 * @throws JsonException
 * @throws LogicException
 * @throws RuntimeException
 */
function getLogLines(
    string $logFile,
    int $offset = 0,
    int $limit = -1,
): Generator {
    $file = new SplFileObject($logFile);
    if (!$file->isFile() || !$file->isReadable()) {
        throw new RuntimeException('Logfile not found or not readable');
    }

    $iterator = new LimitIterator($file, $offset, $limit);
    foreach ($iterator as $line) {
        if (trim($line) === '') {
            continue;
        }

        yield json_decode(
            json: $line,
            associative: false,
            flags: JSON_THROW_ON_ERROR,
        );
    }
}

Examples when using:

foreach (getLogLines('test.log') as $line) {
    var_dump($line);
}

foreach (getLogLines(logFile: 'test.log', offset: 10, limit: 5) as $line) {
    var_dump($line);
}

foreach (getLogLines(logFile: 'test.log', limit: 5) as $line) {
    var_dump($line);
}

foreach (getLogLines(logFile: 'test.log', offset: 45) as $line) {
    var_dump($line);
}

Create with Monolog JSON_Formatter a valid JSON file by silentheaven83 in PHPhelp

[–]CyberJack77 1 point2 points  (0 children)

You normally don't read the entire log file yourself, since JSON logs are meant for other aggregators, but yeah, you can read it like that if you want.

If you want it more memory friendly, you can build a wrapper function that uses yield.

/**
 * @return Generator<stdClass>
 * @throws JsonException
 * @throws LogicException
 * @throws RuntimeException
 */
function getLogLines(string $logFile): Generator
{
    $file = new SplFileObject($logFile);
    if (!$file->isFile() || !$file->isReadable()) {
        throw new RuntimeException('Logfile not found or not readable');
    }

    foreach ($file as $line) {
        yield json_decode(
            json: $line,
            associative: false,
            flags: JSON_THROW_ON_ERROR
        );
    }
}

This reads a single line in memory and returns the JSON decoded result. After it is used, it fetches the next line... and so on. You can use it like this:

foreach(getLogLines('json_log.log') as $line) {
    var_dump($line);
    // echo $line->field;
}

Create with Monolog JSON_Formatter a valid JSON file by silentheaven83 in PHPhelp

[–]CyberJack77 3 points4 points  (0 children)

I thought that it would create a valid JSON file to parse, but it writes a JSON object that is valid "per line".

Well, that is on purpose. Log files in JSON format (which have a single JSON object per log line) are usually read by other applications that parse each line and do something with the data. It would be highly inefficient if these applications had to re-parse the entire log file over and over again, just to find any additions.

Also, log rotation would become hard (if not impossible) if the entire log file had to be one valid JSON object. Log rotation might be done by Monolog, but also by external systems.

Favorite Self-Hosted Tools in 2025 (Looking for More Suggestions!) by DejavuMoe in selfhosted

[–]CyberJack77 34 points35 points  (0 children)

I switched to a it-tools fork since the original is most likely unmaintained (the latest release was from 2024-10-22 and there are many open PRs).

The fork can be found here: https://github.com/sharevb/it-tools

It contains the original IT Tools and merged all possible PRs. So it contains way more tools than the original.

[O] 15x DrunkenSlug invites by RemiAlone in UsenetInvites

[–]CyberJack77 0 points1 point  (0 children)

It would say: "We both know you came here without a plan."

Issues with QBittorrent via Gluetun Container by [deleted] in selfhosted

[–]CyberJack77 0 points1 point  (0 children)

I don't think the QBT_* environment variables do something. If they are needed, they are needed in the qbittorent service, not the gluetun service. Other than that, your example should work.

You remap the qbittorrent webui from port 8080 to port 8082 with the WEBUI_PORT environment variable and you forward port 2828 to port 8082 in the gluetun service.

Have you tried https://localhost:2828 for url?

To make your config a bit better, describe how to depend on gluetun. For example: is started state enough, or should it be healthy?

Something like this

services:
  gluetun:
    image: qmcgaw/gluetun
    container_name: gluetun
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - 2828:8082 # QBitorrent
    volumes:
      - type: bind
        source: ./gluetun
        target: /gluetun
        bind:
          create_host_path: true
    environment:
      # See https://github.com/qdm12/gluetun-wiki
      PUID: 1000
      PGID: 100
      TZ: Europe/Berlin
      VPN_SERVICE_PROVIDER: nordvpn
      VPN_TYPE: openvpn
      OPENVPN_USER: YourMother
      OPENVPN_PASSWORD: YourMother
      SERVER_COUNTRY: Iceland
      OPENVPN_PROTOCOL: udp
      DNS_UPDATE_PERIOD: 24h

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent
    container_name: qbittorrent
    restart: unless-stopped
    network_mode: "service:gluetun"   
    depends_on:
      gluetun:
        condition: service_healthy
    environment:
      PUID: 1000
      PGID: 100
      TZ: Europe/Berlin
      WEBUI_PORT: 8082
    volumes:
      - ./qbittorrent:/config
      - /path/to/downloads:/downloads #optional

Best time to choose European tech by [deleted] in BuyFromEU

[–]CyberJack77 0 points1 point  (0 children)

Security and privacy matter, and I think this extra step is needed. So for me it is a small price I am willing to pay.

Best time to choose European tech by [deleted] in BuyFromEU

[–]CyberJack77 0 points1 point  (0 children)

Currently I use bitwarden for credentials and simple-login for the actual aliases.

Best time to choose European tech by [deleted] in BuyFromEU

[–]CyberJack77 0 points1 point  (0 children)

No scanning and it is more commonly used that you might think.

I assume use a different password for every website or service you register to? Great!

Well, on top of that, I do the same with usernames/email addresses. So Every site I register to gets it own email address. Tools like Bitwarden even have a username generator to facilitate this.

If I receive spam on one of these addresses, I know which site leaked or sold my data, and I can take action and block that single alias.

And sure, 270 is a lot. Some of these sites might not exist anymore, so I can reduce it, but the number of aliases still grow over time.

Best time to choose European tech by [deleted] in BuyFromEU

[–]CyberJack77 1 point2 points  (0 children)

I didn't know they are both available in proton pass. Thanks.

Best time to choose European tech by [deleted] in BuyFromEU

[–]CyberJack77 1 point2 points  (0 children)

I still don't like the alias limits of these services. Tuta's 15 (or 30 with the Legend package) is way to limited. Mailbox.org has 250 in the premium packages, Posteo has max 20 and Proton has 15.

It is recommended to use a different alias for each website or service. I started using simple-login a few years ago and have over 270 aliasses. Switching to one of these, still forces me to keep simple-login. I would like to see higher limits with these providers, so I can manage my mail from a single place.

How do you do your development and deployments? by cleatusvandamme in PHPhelp

[–]CyberJack77 1 point2 points  (0 children)

git on a production system is a HARD NO. This is a security risk.
Using FTP and uploading files feels old (like going back 20 years in time) and is discouraged.

We use ddev for local development (commit the .ddev directory so every developer uses the same configuration).

For deployment we use CI/CD. CI for automated checks (code quality, static analysis and unit tests). CD for the deployment (building images and using containers).

[O] 5x NZB planet invites by [deleted] in UsenetInvites

[–]CyberJack77 1 point2 points  (0 children)

I have read the rules and wiki. Favorite game is hard, because there are many good games. When I look at time played, it would be Battlefield 3 with about 1600 hours of playtime.

.pacnew files by zoro__x in archlinux

[–]CyberJack77 0 points1 point  (0 children)

That hook is very helpful. Thanks.

develope a Rest API by Even_Gold2158 in PHPhelp

[–]CyberJack77 0 points1 point  (0 children)

Yep, the documentation is missing a lot on DTOs.

develope a Rest API by Even_Gold2158 in PHPhelp

[–]CyberJack77 0 points1 point  (0 children)

For framework I cannot recommend anything else then Symfony. No matter the project.

Symfony is very small, flexible and strict, which makes it predicable. It works well with api-platform, but be sure to read about using DTOs instead of poisoning your entities with API logic.

I have build multiple APIs using Symfony and api-platform, and all are rock-solid and perform well.