API Modul Update (Perfex CRM)
Perfex CRM – REST API Modul: Upgrade von 2.0.8 auf 3.1.4
1. Ausgangssituation
Das Modul REST API for Perfex CRM von Themesic Interactive sollte von Version 2.0.8 auf 3.1.4 aktualisiert werden.
Systemumgebung
| Komponente | Konfiguration |
|---|---|
| Anwendung | Perfex CRM |
| Modul | api – REST API for Perfex CRM |
| Ausgangsversion | 2.0.8 |
| Zielversion | 3.1.4 |
| PHP | 8.3 |
| Webserver | Nginx |
| Hostingverwaltung | ISPConfig |
| PHP-FPM-Pool | webXXX |
| Modulverzeichnis | modules/api/ |
Das Upgrade wurde über Perfex CRM → Setup → Modules → API → Datenbank aktualisieren gestartet.
2. Problem 1: Lücke in der Migrationssequenz
Fehlermeldung
There is a gap in the migration sequence near version number: 300.
Analyse
Die Migrationsverwaltung befindet sich in:
application/libraries/App_module_migration.php
Der Ablauf wurde anhand des Quellcodes untersucht.
Die installierte Modulversion wird aus tblmodules.installed_version gelesen. Die Versionsnummer wird dabei ohne Punkte verarbeitet:
2.0.8 → 208
3.1.4 → 314
Das Migrationssystem erwartet fortlaufende dreistellige Migrationsnummern.
Im Verzeichnis modules/api/migrations/ waren unter anderem folgende Dateien vorhanden:
200_version_200.php
...
216_version_216.php
300_version_300.php
...
314_version_314.php
Die Dateien für die Versionen 217 bis 299 fehlten.
Dadurch erkannte das Migrationssystem einen Sprung von 216 auf 300 und verweigerte das Upgrade.
Durchgeführte Lösung
Es wurden 83 Platzhaltermigrationen für die Nummern 217–299 erstellt.
Beispiel:
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Migration_Version_217 extends App_module_migration
{
public function up()
{
// Placeholder migration - no database changes.
}
}
Diese Platzhalter stellen die fortlaufende Nummerierung her, führen aber selbst keine Datenbankänderungen durch.
Anschließend konnte die Migrationsverwaltung die Sequenz über die ursprüngliche Lücke hinaus verarbeiten.
Wichtige Einschränkung
Es wurde nicht nachgewiesen, dass die fehlenden Versionen 217–299 tatsächlich keine Datenbankänderungen enthalten sollten.
Die Platzhalter waren ein technischer Workaround. Die offizielle Migrationshistorie sollte mit dem Modulhersteller abgeglichen werden.
3. Problem 2: HTTP 500 beim Datenbank-Upgrade
Nach dem Schließen der Migrationslücke trat beim erneuten Upgrade ein neuer Fehler auf:
HTTP 500 Internal Server Error
Die installierte Modulversion blieb zunächst unverändert:
api | 2.0.8 | 1
Erste Diagnose
Zunächst wurden die üblichen Protokolle überprüft:
- Perfex-Logverzeichnis
application/logs/ - Nginx-Fehlerprotokoll
- PHP-FPM-Log
- Systemjournal über
journalctl
Dabei wurde kein passender PHP-Fatal-Error gefunden.
Zusätzlich wurden alle PHP-Migrationsdateien auf Syntaxfehler und doppelte Klassennamen geprüft.
Ergebnis:
- Keine PHP-Syntaxfehler.
- Keine doppelten Migrationsklassen.
- Die Migrationen 209–216 enthielten jeweils
up()unddown(). - Der Fehler ließ sich über die Standardprotokolle nicht identifizieren.
4. Problem 3: PHP-Fehler wurden nicht protokolliert
Um den HTTP-500-Fehler sichtbar zu machen, wurde temporär ein separates PHP-Fehlerprotokoll eingerichtet.
Temporäre PHP-Konfiguration
Über .user.ini:
log_errors = On
display_errors = Off
error_log = /var/www/clients/client1/web20/private/php-api-debug.log
Die Protokolldatei wurde mit passenden Dateirechten angelegt.
Anschließend wurde PHP-FPM neu geladen:
sudo systemctl reload php8.3-fpm
Funktionstest
Mit einer temporären PHP-Testdatei wurde geprüft, ob error_log() tatsächlich in die neue Protokolldatei schreibt.
Der Test war erfolgreich.
Allerdings blieb das Fehlerprotokoll beim nächsten Upgradeversuch zunächst leer.
Daher musste die eigentliche Upgrade-Logik untersucht werden.
5. Problem 4: Fehlerquelle im Perfex-Code lokalisieren
Zuständigen Controller finden
Der zuerst vermutete Controller Modules.php existierte in dieser Installation nicht.
Die tatsächliche Methode wurde mit folgendem Befehl gefunden:
grep -Rnl 'function upgrade_database' \
application/ modules/ \
--include='*.php' 2>/dev/null
Relevante Dateien:
application/controllers/admin/Mods.php
application/libraries/App_modules.php
application/libraries/App_module_migration.php
Controller
In application/controllers/admin/Mods.php:
public function upgrade_database($name)
{
$result = $this->app_modules->upgrade_database($name);
if (is_string($result)) {
set_alert('danger', $result);
} else {
set_alert('success', 'Database Upgraded Successfully');
}
$this->to_modules();
}
Der Controller führt die Migration nicht selbst aus, sondern delegiert sie an App_modules.
Modulverwaltung
In application/libraries/App_modules.php:
public function upgrade_database($name)
{
$migration = new App_module_migration($name);
if ($migration->to_latest() === false) {
return $migration->error_string();
}
return true;
}
Damit war klar, dass der Fehler während der Migration oder einer darunterliegenden Datenbankoperation auftreten musste.
6. Lösung: Temporäre Debug-Instrumentierung
Um die genaue Fehlerstelle sichtbar zu machen, wurde App_modules.php zunächst gesichert:
cp -a application/libraries/App_modules.php \
application/libraries/App_modules.php.bak
Anschließend wurde upgrade_database() temporär durch eine Variante mit zusätzlichem Logging ersetzt:
public function upgrade_database($name)
{
error_log('API DEBUG: upgrade_database start: ' . $name);
try {
$migration = new App_module_migration($name);
error_log('API DEBUG: migration initialized');
$result = $migration->to_latest();
error_log(
'API DEBUG: migration result: ' .
var_export($result, true)
);
if ($result === false) {
return $migration->error_string();
}
return true;
} catch (\Throwable $e) {
error_log('API DEBUG ERROR: ' . $e->getMessage());
error_log($e->getTraceAsString());
throw $e;
}
}
Nach einem weiteren Upgradeversuch lieferte das Debug-Protokoll erstmals die konkrete Fehlermeldung.
Tatsächlicher Fehler
Can't create table `c1crmdb`.`tbluser_api_quotas`
(errno: 150 "Foreign key constraint is incorrectly formed")
Der Stacktrace zeigte die Fehlerstelle:
modules/api/migrations/209_version_209.php:23
Damit war der HTTP-500-Fehler eindeutig auf Migration 209 zurückzuführen.
7. Problem 5: Fehlerhafter Foreign Key in Migration 209
Ursache
Migration 209 sollte die Tabelle tbluser_api_quotas erstellen.
Dabei wurde folgende Spalte definiert:
`user_api_id` int(11) NOT NULL
Anschließend sollte ein Fremdschlüssel zur bestehenden Tabelle tbluser_api angelegt werden:
CONSTRAINT `fk_user_api_quotas_user_api`
FOREIGN KEY (`user_api_id`)
REFERENCES `tbluser_api` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE
Die Datenbankprüfung ergab jedoch:
tbluser_api.id = INT(11) UNSIGNED
Somit waren die Datentypen nicht identisch:
| Tabelle | Spalte | Ursprünglicher Typ |
|---|---|---|
tbluser_api |
id |
INT(11) UNSIGNED |
tbluser_api_quotas |
user_api_id |
INT(11) |
MariaDB verlangt für solche Fremdschlüssel übereinstimmende Ganzzahltypen einschließlich der UNSIGNED-Eigenschaft.
Der Unterschied verhinderte das Anlegen der Tabelle.
Lösung
In der Datei:
modules/api/migrations/209_version_209.php
wurden die Definitionen von user_api_id auf INT UNSIGNED angepasst.
Vorher:
`user_api_id` int(11) NOT NULL
Nachher:
`user_api_id` int(11) UNSIGNED NOT NULL
Die Änderung betraf vier Stellen:
- Erstellung von
tbluser_api_quotas. - Nachträgliches Ergänzen der Spalte in
tbluser_api_quotas. - Erstellung von
tblapi_usage_logs. - Nachträgliches Ergänzen der Spalte in
tblapi_usage_logs.
Shell-Befehl
cp -a modules/api/migrations/209_version_209.php \
modules/api/migrations/209_version_209.php.bak
sed -i \
's/`user_api_id` int(11) NOT NULL/`user_api_id` int(11) UNSIGNED NOT NULL/g' \
modules/api/migrations/209_version_209.php
Anschließende Kontrolle:
grep -n 'user_api_id.*int(11)' \
modules/api/migrations/209_version_209.php
Die vier Definitionen sollten danach UNSIGNED enthalten.
8. Problem 6: Bereits vorhandene Tabelle tblapi_usage_logs
Während der Analyse stellte sich heraus, dass die Tabelle tblapi_usage_logs bereits existierte.
Vor einer erneuten Ausführung der Migration musste deshalb geprüft werden, ob diese Tabelle ebenfalls einen fehlerhaften Spaltentyp oder einen fehlenden Fremdschlüssel besitzt.
Prüfung des Datentyps
In phpMyAdmin:
SHOW COLUMNS
FROM tblapi_usage_logs
LIKE 'user_api_id';
Ergebnis:
user_api_id | int(11) unsigned | NO | MUL
Der Datentyp war bereits korrekt.
Prüfung des Foreign Keys
Über information_schema.KEY_COLUMN_USAGE:
SELECT
CONSTRAINT_NAME,
COLUMN_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'c1crmdb'
AND TABLE_NAME = 'tblapi_usage_logs'
AND CONSTRAINT_NAME = 'fk_api_usage_logs_user_api';
Ergebnis:
| Eigenschaft | Wert |
|---|---|
| Constraint | fk_api_usage_logs_user_api |
| Spalte | user_api_id |
| Referenzierte Tabelle | tbluser_api |
| Referenzierte Spalte | id |
Ergebnis: Die vorhandene Tabelle war korrekt aufgebaut und musste nicht verändert werden.
9. Ergebnis: Datenbank-Upgrade erfolgreich
Nach der Korrektur der Fremdschlüsselspalten in Migration 209 wurde das Datenbank-Upgrade erneut ausgeführt.
Das Upgrade lief anschließend erfolgreich durch.
Damit waren die beiden unmittelbar blockierenden Probleme behoben:
- Die Lücke in der Migrationssequenz wurde durch Platzhalter geschlossen.
- Der fehlerhafte Datentyp in Migration 209 wurde korrigiert.
Die erfolgreiche Durchführung wurde im Perfex-Backend gemeldet. Eine abschließende SQL-Ausgabe der gespeicherten Version und ein vollständiger API-Funktionstest sind im dokumentierten Verlauf noch nicht enthalten.
10. Aufräumarbeiten nach dem Upgrade
Temporäre Änderungen an Perfex zurücknehmen
Die Debug-Version von App_modules.php sollte durch die Originaldatei ersetzt werden:
cp -a application/libraries/App_modules.php.bak \
application/libraries/App_modules.php
Anschließend die temporäre PHP-Testdatei entfernen und die für die Fehlersuche hinzugefügten .user.ini-Einstellungen zurücknehmen.
Die korrigierte Migration 209 sollte dagegen als dokumentierter Patch erhalten bleiben.
Version in der Datenbank kontrollieren
SELECT
module_name,
installed_version,
active
FROM tblmodules
WHERE module_name = 'api';
Erwartetes Ergebnis:
api | 3.1.4 | 1
API testen
Mindestens folgende Funktionen sollten überprüft werden:
- API-Authentifizierung mit einem gültigen Token.
- Ein lesender API-Endpunkt.
- API-Quotas und Nutzungsprotokollierung, soweit aktiviert.
- Bestehende Integrationen, die das Modul verwenden.
11. Zusammenfassung aller Probleme
| Nr. | Problem | Ursache | Lösung | Ergebnis |
|---|---|---|---|---|
| 1 | Migration gap near 300 | Migrationen 217–299 fehlten | 83 Platzhaltermigrationen erstellt | Sequenzprüfung überwunden |
| 2 | HTTP 500 | Fehler während der Datenbankmigration | Upgrade-Code instrumentiert | Ursache gefunden |
| 3 | Kein PHP-Fehler im Log | Fehler nicht im üblichen PHP-Protokoll sichtbar | Separates Log und try/catch |
Stacktrace verfügbar |
| 4 | Controller zunächst nicht gefunden | Tatsächlicher Controller heißt Mods.php |
Quellcode durchsucht | Upgrade-Aufruf lokalisiert |
| 5 | MariaDB-Fehler 150 | INT statt INT UNSIGNED bei Foreign Key |
Migration 209 korrigiert | Tabellenanlage möglich |
| 6 | Bestehende API-Logtabelle | Struktur musste vor erneutem Upgrade geprüft werden | Spaltentyp und Foreign Key kontrolliert | Keine Änderung erforderlich |
| 7 | Upgrade zunächst blockiert | Kombination aus Migrationslücke und fehlerhaftem SQL | Beide Ursachen behoben | Upgrade erfolgreich |
12. Erkenntnisse für zukünftige Perfex-Modulupdates
Migrationen vor dem Upgrade prüfen
Vor einem größeren Versionssprung sollten folgende Punkte kontrolliert werden:
- Sind alle benötigten Migrationsdateien vorhanden?
- Stimmen die Migrationsnummern mit der installierten Version überein?
- Gibt es Sprünge in der Nummerierung?
- Stimmen Fremdschlüsselspalten mit den vorhandenen Datenbankspalten überein?
- Enthalten die Migrationen Änderungen, die mit einer älteren Datenbankstruktur kollidieren könnten?
Fehler gezielt protokollieren
Wenn ein Upgrade mit HTTP 500 abbricht, aber keine PHP-Fehler protokolliert werden, kann eine temporäre Instrumentierung direkt am Migrationsaufruf sinnvoll sein.
Besonders hilfreich waren hier:
catch (\Throwable $e) {
error_log($e->getMessage());
error_log($e->getTraceAsString());
throw $e;
}
Dadurch wurde aus einem unspezifischen HTTP-500-Fehler eine konkrete Datenbankfehlermeldung mit Dateiname und Zeilennummer.
Datenbankversion nicht manuell erhöhen
Ein manuelles Ändern von tblmodules.installed_version hätte die Migrationen übersprungen und möglicherweise eine inkonsistente Datenbank hinterlassen.
Stattdessen wurde die eigentliche Ursache behoben und der reguläre Upgrade-Prozess verwendet.
Offener Punkt: Platzhaltermigrationen 217–299
Die wichtigste noch offene technische Frage betrifft die fehlenden Migrationen 217–299.
Es sollte beim Modulhersteller geklärt werden, ob diese Versionen absichtlich ausgelassen wurden oder ob das Updatepaket unvollständig war.
Solange das nicht bestätigt ist, sollte das erfolgreiche Upgrade nicht als Nachweis für eine vollständig geprüfte Datenbankmigration betrachtet werden.
Status: Upgrade erfolgreich durchgeführt. Nachkontrolle und Abgleich der fehlenden Herstellermigrationen empfohlen.
