Compare commits
11 Commits
acf0e62a73
...
8a48c3f17f
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a48c3f17f | |||
| 0d5ed4de1e | |||
| 5af979cefe | |||
| fe2a8fbfa3 | |||
| ea09a821ab | |||
| 44584fc306 | |||
| 452048de64 | |||
| a7a94f93d8 | |||
| 2b89e50b77 | |||
| 4d758df68e | |||
| 2cd85c3ffd |
@@ -11,7 +11,7 @@
|
||||
"mongodb/mongodb": "^2.1",
|
||||
"php-di/php-di": "*",
|
||||
"phpseclib/phpseclib": "^3.0",
|
||||
"symfony/console": "^7.0"
|
||||
"symfony/console": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^11.0"
|
||||
|
||||
54
composer.lock
generated
54
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "b14b010c422e222aeb1e33104e2a09ec",
|
||||
"content-hash": "f77626277cdeb9207c6aab5d2d9a19b2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
@@ -606,47 +606,39 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.4.8",
|
||||
"version": "v8.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707"
|
||||
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707",
|
||||
"reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/5b66d385dc58f69652e56f78a4184615e3f2b7f7",
|
||||
"reference": "5b66d385dc58f69652e56f78a4184615e3f2b7f7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"php": ">=8.4",
|
||||
"symfony/polyfill-mbstring": "^1.0",
|
||||
"symfony/service-contracts": "^2.5|^3",
|
||||
"symfony/string": "^7.2|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<6.4",
|
||||
"symfony/dotenv": "<6.4",
|
||||
"symfony/event-dispatcher": "<6.4",
|
||||
"symfony/lock": "<6.4",
|
||||
"symfony/process": "<6.4"
|
||||
"symfony/string": "^7.4|^8.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0|2.0|3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/config": "^6.4|^7.0|^8.0",
|
||||
"symfony/dependency-injection": "^6.4|^7.0|^8.0",
|
||||
"symfony/event-dispatcher": "^6.4|^7.0|^8.0",
|
||||
"symfony/http-foundation": "^6.4|^7.0|^8.0",
|
||||
"symfony/http-kernel": "^6.4|^7.0|^8.0",
|
||||
"symfony/lock": "^6.4|^7.0|^8.0",
|
||||
"symfony/messenger": "^6.4|^7.0|^8.0",
|
||||
"symfony/process": "^6.4|^7.0|^8.0",
|
||||
"symfony/stopwatch": "^6.4|^7.0|^8.0",
|
||||
"symfony/var-dumper": "^6.4|^7.0|^8.0"
|
||||
"symfony/config": "^7.4|^8.0",
|
||||
"symfony/dependency-injection": "^7.4|^8.0",
|
||||
"symfony/event-dispatcher": "^7.4|^8.0",
|
||||
"symfony/http-foundation": "^7.4|^8.0",
|
||||
"symfony/http-kernel": "^7.4|^8.0",
|
||||
"symfony/lock": "^7.4|^8.0",
|
||||
"symfony/messenger": "^7.4|^8.0",
|
||||
"symfony/process": "^7.4|^8.0",
|
||||
"symfony/stopwatch": "^7.4|^8.0",
|
||||
"symfony/var-dumper": "^7.4|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -680,7 +672,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.4.8"
|
||||
"source": "https://github.com/symfony/console/tree/v8.0.8"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -700,7 +692,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-30T13:54:39+00:00"
|
||||
"time": "2026-03-30T15:14:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
@@ -3148,7 +3140,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@@ -3156,6 +3148,6 @@
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
|
||||
@@ -57,15 +57,15 @@ return [
|
||||
'per_tenant' => false,
|
||||
|
||||
// ── file driver options ────────────────────────────────────────────────
|
||||
// Absolute path to the log directory. null = <project_root>/var/log
|
||||
// Absolute path to the log directory. null = <project_root>/var/logs
|
||||
'path' => null,
|
||||
|
||||
// Log channel — used as the filename without extension.
|
||||
// 'app' → var/log/app.jsonl (or var/log/tenant/{id}/app.jsonl when per_tenant = true)
|
||||
// 'app' → var/logs/app.jsonl (or var/logs/tenant/{id}/app.jsonl when per_tenant = true)
|
||||
'channel' => 'app',
|
||||
|
||||
// ── syslog driver options ──────────────────────────────────────────────
|
||||
// Identity tag passed to openlog(); visible in /var/log/syslog and journalctl -t ktrix
|
||||
// Identity tag passed to openlog(); visible in /var/logs/syslog and journalctl -t ktrix
|
||||
'ident' => 'ktrix',
|
||||
|
||||
// openlog() facility constant. Common values: LOG_USER, LOG_LOCAL0 … LOG_LOCAL7
|
||||
|
||||
@@ -79,7 +79,7 @@ class LoggerFactory
|
||||
$path = $logConfig['path'] ?? null;
|
||||
|
||||
if ($path === null || $path === '') {
|
||||
$path = $projectDir . '/var/log';
|
||||
$path = $projectDir . '/var/logs';
|
||||
}
|
||||
|
||||
return new FileLogger($path, $channel);
|
||||
|
||||
@@ -20,10 +20,8 @@ class PlainFileLogger implements LoggerInterface
|
||||
*/
|
||||
public function __construct(string $logDir, string $channel = 'app')
|
||||
{
|
||||
if (!is_dir($logDir)) {
|
||||
@mkdir($logDir, 0775, true);
|
||||
}
|
||||
$this->logFile = rtrim($logDir, '/') . '/' . $channel . '.log';
|
||||
$this->ensureWritablePath();
|
||||
}
|
||||
|
||||
public function emergency($message, array $context = []): void { $this->log('emergency', $message, $context); }
|
||||
@@ -37,12 +35,38 @@ class PlainFileLogger implements LoggerInterface
|
||||
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
$this->ensureWritablePath();
|
||||
|
||||
$dt = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6F', microtime(true)));
|
||||
$timestamp = $dt?->format('Y-m-d H:i:s.u') ?? date('Y-m-d H:i:s');
|
||||
|
||||
$line = $timestamp . ' ' . $this->interpolate((string) $message, $context) . PHP_EOL;
|
||||
|
||||
@file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
|
||||
if (@file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX) === false) {
|
||||
error_log(sprintf('Failed to write to log file: %s', $this->logFile));
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureWritablePath(): void
|
||||
{
|
||||
$logDir = dirname($this->logFile);
|
||||
|
||||
if (!is_dir($logDir)) {
|
||||
@mkdir($logDir, 0777, true);
|
||||
}
|
||||
|
||||
if (is_dir($logDir)) {
|
||||
@chmod($logDir, 0777);
|
||||
}
|
||||
|
||||
if (!file_exists($this->logFile)) {
|
||||
@touch($this->logFile);
|
||||
}
|
||||
|
||||
clearstatcache(true, $this->logFile);
|
||||
if (file_exists($this->logFile)) {
|
||||
@chmod($this->logFile, 0666);
|
||||
}
|
||||
}
|
||||
|
||||
private function interpolate(string $message, array $context): string
|
||||
|
||||
1296
package-lock.json
generated
1296
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@
|
||||
"vue": "3.5.28",
|
||||
"vue-router": "5.0.2",
|
||||
"vue3-perfect-scrollbar": "2.0.0",
|
||||
"vuetify": "3.11.8"
|
||||
"vuetify": "4.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.18.0",
|
||||
@@ -56,8 +56,8 @@
|
||||
"sass-loader": "16.0.7",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "^8.59.0",
|
||||
"vite": "7.3.1",
|
||||
"vite-plugin-static-copy": "^3.4.0",
|
||||
"vite": "8.0.10",
|
||||
"vite-plugin-static-copy": "^4.0.0",
|
||||
"vitest": "^4.0.18",
|
||||
"vue-cli-plugin-vuetify": "2.5.8",
|
||||
"vue-tsc": "^3.2.7"
|
||||
|
||||
@@ -20,6 +20,7 @@ use JsonSerializable;
|
||||
*/
|
||||
enum CollectionRoles: string implements JsonSerializable {
|
||||
|
||||
case None = '';
|
||||
case Inbox = 'inbox';
|
||||
case Drafts = 'drafts';
|
||||
case Sent = 'sent';
|
||||
@@ -28,7 +29,6 @@ enum CollectionRoles: string implements JsonSerializable {
|
||||
case Archive = 'archive';
|
||||
case Outbox = 'outbox';
|
||||
case Queue = 'queue';
|
||||
case Custom = 'custom';
|
||||
|
||||
public function jsonSerialize(): string {
|
||||
return $this->value;
|
||||
|
||||
@@ -38,6 +38,7 @@ interface ServiceBaseInterface extends ResourceServiceBaseInterface {
|
||||
// Collection Filter
|
||||
public const CAPABILITY_COLLECTION_FILTER_LABEL = 'label';
|
||||
public const CAPABILITY_COLLECTION_FILTER_ROLE = 'role';
|
||||
public const CAPABILITY_COLLECTION_FILTER_SUBSCRIBED = 'subscribed';
|
||||
// Collection Sort
|
||||
public const CAPABILITY_COLLECTION_SORT_LABEL = 'label';
|
||||
public const CAPABILITY_COLLECTION_SORT_RANK = 'rank';
|
||||
|
||||
@@ -68,11 +68,10 @@ interface ServiceCollectionMutableInterface extends ServiceBaseInterface {
|
||||
*
|
||||
* @param string|int $identifier Collection ID
|
||||
* @param bool $force Force deletion even if not empty
|
||||
* @param bool $recursive Recursively delete contents
|
||||
*
|
||||
* @return bool True if deleted
|
||||
* @return CollectionBaseInterface|true Collection object on soft delete, true on hard delete
|
||||
*/
|
||||
public function collectionDelete(string|int $identifier, bool $force = false, bool $recursive = false): bool;
|
||||
public function collectionDelete(string|int $identifier, bool $force = false): CollectionBaseInterface | true;
|
||||
|
||||
/**
|
||||
* Moves a collection to a new parent
|
||||
|
||||
Reference in New Issue
Block a user