Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be934e9f1d |
@@ -23,8 +23,8 @@ use KTXF\Resource\Identifier\EntityIdentifier;
|
||||
use KTXF\Resource\Identifier\ResourceIdentifier;
|
||||
use KTXF\Resource\Identifier\ResourceIdentifiers;
|
||||
use KTXF\Resource\Identifier\ServiceIdentifier;
|
||||
use KTXF\Mail\Provider\ProviderBaseInterface;
|
||||
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
|
||||
use KTXF\Resource\SystemIdentity;
|
||||
use KTXF\Routing\Attributes\AuthenticatedRoute;
|
||||
use KTXM\MailManager\Manager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -86,7 +86,7 @@ class DefaultController extends ControllerAbstract {
|
||||
// acting-user override: only the reserved system context is permitted,
|
||||
// gated on the system mail management permission
|
||||
if ($user !== null && $user !== $userId) {
|
||||
if ($user !== SystemIdentity::USER || !$this->identityContext->hasPermission('mail_manager.system')) {
|
||||
if ($user !== ProviderBaseInterface::USER_SYSTEM || !$this->identityContext->hasPermission('mail_manager.system')) {
|
||||
return new JsonResponse([
|
||||
'version' => $version,
|
||||
'transaction' => $transaction,
|
||||
@@ -180,7 +180,6 @@ class DefaultController extends ControllerAbstract {
|
||||
'entity.copy' => throw new InvalidArgumentException('Operation not implemented: ' . $operation),
|
||||
'entity.submit' => $this->entitySubmit($tenantId, $userId, $data),
|
||||
'entity.download' => $this->entityDownload($tenantId, $userId, $data),
|
||||
'entity.preview' => $this->entityPreview($tenantId, $userId, $data),
|
||||
'entity.blobs' => $this->entityBlobs($tenantId, $userId, $data),
|
||||
|
||||
default => throw new InvalidArgumentException(self::ERR_INVALID_OPERATION . $operation)
|
||||
@@ -969,36 +968,4 @@ class DefaultController extends ControllerAbstract {
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function entityPreview(string $tenantId, string $userId, array $data): Response {
|
||||
if (!isset($data['target']) || !is_string($data['target'])) {
|
||||
throw new InvalidArgumentException(self::ERR_MISSING_TARGET);
|
||||
}
|
||||
if (!isset($data['part']) || !is_array($data['part'])) {
|
||||
throw new InvalidArgumentException('Attachment preview requires a part');
|
||||
}
|
||||
|
||||
$target = ResourceIdentifier::fromString($data['target']);
|
||||
if (!$target instanceof EntityIdentifier) {
|
||||
throw new InvalidArgumentException(self::ERR_INVALID_IDENTIFIER);
|
||||
}
|
||||
$preview = $this->manager->entityPreview(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$target,
|
||||
$data['part'],
|
||||
isset($data['variant']) && is_string($data['variant']) ? $data['variant'] : 'inline',
|
||||
);
|
||||
if ($preview === null) {
|
||||
return new Response('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
return new StreamedResponse($preview->resource->stream(), Response::HTTP_OK, [
|
||||
'Content-Type' => $preview->resource->mimeType(),
|
||||
'Content-Length' => (string) $preview->size,
|
||||
'ETag' => '"' . trim($preview->etag, '"') . '"',
|
||||
'Cache-Control' => 'private, max-age=3600',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-50
@@ -5,10 +5,7 @@ declare(strict_types=1);
|
||||
namespace KTXM\MailManager;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use KTXC\Preview\Preview;
|
||||
use KTXC\Preview\PreviewManager;
|
||||
use KTXC\Resource\ProviderManager;
|
||||
use KTXF\Preview\PreviewSource;
|
||||
use KTXF\Mail\Collection\CollectionBaseInterface;
|
||||
use KTXF\Mail\Collection\CollectionPropertiesMutableInterface;
|
||||
use KTXF\Mail\Collection\ICollectionBase;
|
||||
@@ -50,7 +47,6 @@ class Manager {
|
||||
public function __construct(
|
||||
private LoggerInterface $logger,
|
||||
private ProviderManager $providerManager,
|
||||
private PreviewManager $previewManager,
|
||||
) { }
|
||||
|
||||
/**
|
||||
@@ -938,6 +934,13 @@ class Manager {
|
||||
}
|
||||
}
|
||||
|
||||
public function entityDownload(string $tenantId, string $userId, EntityIdentifier $targetEntity, array|null $targetPart): BinaryResource {
|
||||
// retrieve service
|
||||
$service = $this->serviceFetch($tenantId, $userId, $targetEntity->provider(), $targetEntity->service());
|
||||
// download entity
|
||||
return $service->entityDownload($targetEntity, $targetPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if messages exist
|
||||
*
|
||||
@@ -1349,50 +1352,4 @@ class Manager {
|
||||
return $service->entitySubmit($sender, $source, $message);
|
||||
}
|
||||
|
||||
public function entityDownload(string $tenantId, string $userId, EntityIdentifier $targetEntity, array|null $targetPart): BinaryResource {
|
||||
// retrieve service
|
||||
$service = $this->serviceFetch($tenantId, $userId, $targetEntity->provider(), $targetEntity->service());
|
||||
// download entity
|
||||
return $service->entityDownload($targetEntity, $targetPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an authorized attachment and lazily generate or retrieve its preview.
|
||||
*/
|
||||
public function entityPreview(string $tenantId, string $userId, EntityIdentifier $targetEntity, array $part, string $variant = 'inline'): ?Preview {
|
||||
$mimeType = $part['type'] ?? null;
|
||||
$size = $part['size'] ?? null;
|
||||
if (!is_string($mimeType) || ($size !== null && !is_int($size))) {
|
||||
throw new InvalidArgumentException('Attachment preview requires a MIME type and optional integer size');
|
||||
}
|
||||
|
||||
$selector = array_filter([
|
||||
'blobId' => $part['blobId'] ?? null,
|
||||
'partId' => $part['partId'] ?? null,
|
||||
'cid' => $part['cid'] ?? null,
|
||||
], static fn(mixed $value): bool => is_string($value) && $value !== '');
|
||||
if ($selector === []) {
|
||||
throw new InvalidArgumentException('Attachment preview requires a blob, part, or content identifier');
|
||||
}
|
||||
|
||||
$attachmentIdentity = hash('sha256', json_encode($selector, JSON_THROW_ON_ERROR));
|
||||
|
||||
return $this->previewManager->fetch($tenantId, new PreviewSource(
|
||||
sourceType: 'mail-attachment',
|
||||
identity: (string) $targetEntity . ':' . $attachmentIdentity,
|
||||
// Attachments are immutable once delivered, so there is no revision to track;
|
||||
// existence and access are enforced by entityDownload() (via serviceFetch) below,
|
||||
// which only runs on a cache miss.
|
||||
signature: 'immutable',
|
||||
mimeType: $mimeType,
|
||||
size: $size,
|
||||
resourceFactory: fn(): BinaryResource => $this->entityDownload(
|
||||
$tenantId,
|
||||
$userId,
|
||||
$targetEntity,
|
||||
$selector,
|
||||
),
|
||||
), $variant);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-8
@@ -2,8 +2,7 @@
|
||||
|
||||
namespace KTXM\MailManager;
|
||||
|
||||
use KTXF\Module\Configuration\BrowserModuleContextInterface;
|
||||
use KTXF\Module\Configuration\ModuleContextInterface;
|
||||
use KTXF\Module\ModuleBrowserInterface;
|
||||
use KTXF\Module\ModuleInstanceAbstract;
|
||||
|
||||
/**
|
||||
@@ -12,7 +11,7 @@ use KTXF\Module\ModuleInstanceAbstract;
|
||||
* Provides unified mail sending and management across multiple providers
|
||||
* with context-aware service discovery and queued delivery.
|
||||
*/
|
||||
class Module extends ModuleInstanceAbstract
|
||||
class Module extends ModuleInstanceAbstract implements ModuleBrowserInterface
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
@@ -59,10 +58,15 @@ class Module extends ModuleInstanceAbstract
|
||||
];
|
||||
}
|
||||
|
||||
public function configure(ModuleContextInterface $context): void
|
||||
{
|
||||
if ($context instanceof BrowserModuleContextInterface) {
|
||||
$context->registerModule($this, 'MailManager', 'static/module.mjs');
|
||||
}
|
||||
public function registerBI(): array {
|
||||
return [
|
||||
'handle' => $this->handle(),
|
||||
'namespace' => 'MailManager',
|
||||
'version' => $this->version(),
|
||||
'label' => $this->label(),
|
||||
'author' => $this->author(),
|
||||
'description' => $this->description(),
|
||||
'boot' => 'static/module.mjs',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+71
-99
@@ -710,6 +710,8 @@
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -725,6 +727,8 @@
|
||||
},
|
||||
"node_modules/@types/chai": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
|
||||
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -734,6 +738,8 @@
|
||||
},
|
||||
"node_modules/@types/deep-eql": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
||||
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -766,12 +772,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz",
|
||||
"integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@bcoe/v8-coverage": "^1.0.2",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/utils": "4.1.11",
|
||||
"ast-v8-to-istanbul": "^1.0.0",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"istanbul-lib-report": "^3.0.1",
|
||||
@@ -785,8 +793,8 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vitest/browser": "4.1.9",
|
||||
"vitest": "4.1.9"
|
||||
"@vitest/browser": "4.1.11",
|
||||
"vitest": "4.1.11"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vitest/browser": {
|
||||
@@ -795,14 +803,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz",
|
||||
"integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "4.1.9",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/spy": "4.1.11",
|
||||
"@vitest/utils": "4.1.11",
|
||||
"chai": "^6.2.2",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
@@ -811,11 +821,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz",
|
||||
"integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "4.1.9",
|
||||
"@vitest/spy": "4.1.11",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.21"
|
||||
},
|
||||
@@ -837,6 +849,8 @@
|
||||
},
|
||||
"node_modules/@vitest/mocker/node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -844,7 +858,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz",
|
||||
"integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -855,11 +871,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz",
|
||||
"integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/utils": "4.1.11",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
@@ -867,12 +885,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz",
|
||||
"integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.9",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/pretty-format": "4.1.11",
|
||||
"@vitest/utils": "4.1.11",
|
||||
"magic-string": "^0.30.21",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
@@ -881,7 +901,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz",
|
||||
"integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -889,11 +911,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/ui": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.11.tgz",
|
||||
"integrity": "sha512-r/rwyKoev21mWdRGSEkZOqkQ2BYy68mwjihg9M90nNRbf4NGrgzZ4cj6JNCEwlOGJkbKeMgsjlykvwKUbRr7gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/utils": "4.1.11",
|
||||
"fflate": "^0.8.2",
|
||||
"flatted": "^3.4.2",
|
||||
"pathe": "^2.0.3",
|
||||
@@ -905,15 +929,17 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vitest": "4.1.9"
|
||||
"vitest": "4.1.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz",
|
||||
"integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.9",
|
||||
"@vitest/pretty-format": "4.1.11",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
@@ -1130,63 +1156,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vuetify/v0": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@vuetify/v0/-/v0-1.2.2.tgz",
|
||||
"integrity": "sha512-7AvM89l6l/pShSYKa+a5/zsht/xj/l4jFpWFOR6UqSelBrPfKxEQ8heO8iCNEGNK5yljwsdARx0JmDJjJRJdVw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/johnleider"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/vuetify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@adobe/leonardo-contrast-colors": ">=1.0.0",
|
||||
"@ant-design/colors": ">=7.0.0",
|
||||
"@flagsmith/flagsmith": ">=11.0.0",
|
||||
"@floating-ui/dom": ">=1.8.0",
|
||||
"@js-temporal/polyfill": ">=0.5.0",
|
||||
"@material/material-color-utilities": ">=0.3.0",
|
||||
"launchdarkly-js-client-sdk": "^3.0.0",
|
||||
"posthog-js": "^1.0.0",
|
||||
"vue": ">=3.5.0 || >=3.6.0-0",
|
||||
"vue-i18n": ">=10.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@adobe/leonardo-contrast-colors": {
|
||||
"optional": true
|
||||
},
|
||||
"@ant-design/colors": {
|
||||
"optional": true
|
||||
},
|
||||
"@flagsmith/flagsmith": {
|
||||
"optional": true
|
||||
},
|
||||
"@floating-ui/dom": {
|
||||
"optional": true
|
||||
},
|
||||
"@js-temporal/polyfill": {
|
||||
"optional": true
|
||||
},
|
||||
"@material/material-color-utilities": {
|
||||
"optional": true
|
||||
},
|
||||
"launchdarkly-js-client-sdk": {
|
||||
"optional": true
|
||||
},
|
||||
"posthog-js": {
|
||||
"optional": true
|
||||
},
|
||||
"vue-i18n": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "2.0.0",
|
||||
"dev": true,
|
||||
@@ -1236,6 +1205,8 @@
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1321,6 +1292,8 @@
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -1381,6 +1354,8 @@
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -2880,17 +2855,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz",
|
||||
"integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.1.9",
|
||||
"@vitest/mocker": "4.1.9",
|
||||
"@vitest/pretty-format": "4.1.9",
|
||||
"@vitest/runner": "4.1.9",
|
||||
"@vitest/snapshot": "4.1.9",
|
||||
"@vitest/spy": "4.1.9",
|
||||
"@vitest/utils": "4.1.9",
|
||||
"@vitest/expect": "4.1.11",
|
||||
"@vitest/mocker": "4.1.11",
|
||||
"@vitest/pretty-format": "4.1.11",
|
||||
"@vitest/runner": "4.1.11",
|
||||
"@vitest/snapshot": "4.1.11",
|
||||
"@vitest/spy": "4.1.11",
|
||||
"@vitest/utils": "4.1.11",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"expect-type": "^1.3.0",
|
||||
"magic-string": "^0.30.21",
|
||||
@@ -2918,12 +2895,12 @@
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "4.1.9",
|
||||
"@vitest/browser-preview": "4.1.9",
|
||||
"@vitest/browser-webdriverio": "4.1.9",
|
||||
"@vitest/coverage-istanbul": "4.1.9",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@vitest/ui": "4.1.9",
|
||||
"@vitest/browser-playwright": "4.1.11",
|
||||
"@vitest/browser-preview": "4.1.11",
|
||||
"@vitest/browser-webdriverio": "4.1.11",
|
||||
"@vitest/coverage-istanbul": "4.1.11",
|
||||
"@vitest/coverage-v8": "4.1.11",
|
||||
"@vitest/ui": "4.1.11",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
@@ -3062,13 +3039,8 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vuetify": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.2.1.tgz",
|
||||
"integrity": "sha512-K3ZIqu7YkorQFB6//dR5KEBpbfOo+y/LkcGtknsnneE3JN1pW3R7o/JB9QDkJuP1ELhENIdBZIkiSmXu0dQ9lQ==",
|
||||
"version": "4.1.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vuetify/v0": "^1.2.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/johnleider"
|
||||
@@ -3076,7 +3048,7 @@
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.7",
|
||||
"vite-plugin-vuetify": ">=2.1.0",
|
||||
"vue": "^3.5.0 || ^3.6.0-0",
|
||||
"vue": "^3.5.0",
|
||||
"webpack-plugin-vuetify": ">=3.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Entity management service
|
||||
*/
|
||||
|
||||
import { transceivePost, transceiveStream, transceiveDownload, transceiveBlob } from './transceive';
|
||||
import { transceivePost, transceiveStream, transceiveDownload } from './transceive';
|
||||
import type {
|
||||
EntityFetchRequest,
|
||||
EntityFetchResponse,
|
||||
@@ -28,7 +28,6 @@ import type {
|
||||
EntityPatchResponse,
|
||||
EntityPatchRequest,
|
||||
EntityDownloadRequest,
|
||||
EntityPreviewRequest,
|
||||
EntityBlobsRequest,
|
||||
EntityBlobsResponse,
|
||||
EntityBlobsWireResponse,
|
||||
@@ -232,10 +231,6 @@ export const entityService = {
|
||||
return transceiveDownload<EntityDownloadRequest>('entity.download', request);
|
||||
},
|
||||
|
||||
async preview(request: EntityPreviewRequest): Promise<Blob> {
|
||||
return await transceiveBlob<EntityPreviewRequest>('entity.preview', request);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch one or more message parts (attachments) inline for rendering.
|
||||
*
|
||||
|
||||
@@ -8,8 +8,9 @@ export { serviceService } from './serviceService';
|
||||
export { collectionService } from './collectionService';
|
||||
export { entityService } from './entityService';
|
||||
|
||||
export * from '../types/common';
|
||||
export * from '../types/provider';
|
||||
export * from '../types/service';
|
||||
export * from '../types/collection';
|
||||
export * from '../types/entity';
|
||||
// Type exports
|
||||
export type * from '../types/common';
|
||||
export type * from '../types/provider';
|
||||
export type * from '../types/service';
|
||||
export type * from '../types/collection';
|
||||
export type * from '../types/entity';
|
||||
|
||||
@@ -188,28 +188,6 @@ export function transceiveDownload<TRequest>(
|
||||
return { transaction: request.transaction };
|
||||
}
|
||||
|
||||
export async function transceiveBlob<TRequest>(operation: string, data: TRequest): Promise<Blob> {
|
||||
const request: ApiRequest<TRequest> = {
|
||||
version: API_VERSION,
|
||||
transaction: generateTransactionId(),
|
||||
operation,
|
||||
data,
|
||||
};
|
||||
let blob: Blob | null = null;
|
||||
await fetchWrapper.post(API_URL, request, {
|
||||
headers: { 'Accept': '*/*' },
|
||||
onStream: async (response: Response) => {
|
||||
if (response.status === 204) {
|
||||
throw new Error(`[${operation}] Preview unavailable`);
|
||||
}
|
||||
blob = await response.blob();
|
||||
},
|
||||
});
|
||||
if (blob === null) throw new Error(`[${operation}] Empty preview response`);
|
||||
return blob;
|
||||
}
|
||||
|
||||
|
||||
function appendHiddenField(form: HTMLFormElement, name: string, value: string): void {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
|
||||
@@ -10,7 +10,6 @@ import type {
|
||||
EntityBlobSelector,
|
||||
EntityBlobsResponse,
|
||||
EntityDownloadRequest,
|
||||
EntityPreviewRequest,
|
||||
EntityTransmitRequest,
|
||||
EntityTransmitResponse,
|
||||
} from '../types/entity'
|
||||
@@ -519,10 +518,6 @@ export const useEntitiesStore = defineStore('mailEntitiesStore', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function preview(request: EntityPreviewRequest): Promise<Blob> {
|
||||
return await entityService.preview(request)
|
||||
}
|
||||
|
||||
// Return public API
|
||||
return {
|
||||
// State (readonly)
|
||||
@@ -545,7 +540,6 @@ export const useEntitiesStore = defineStore('mailEntitiesStore', () => {
|
||||
move,
|
||||
transmit,
|
||||
download,
|
||||
preview,
|
||||
blobs,
|
||||
}
|
||||
})
|
||||
|
||||
+1
-7
@@ -227,12 +227,6 @@ export interface EntityDownloadRequest {
|
||||
filename?: string | null;
|
||||
}
|
||||
|
||||
export interface EntityPreviewRequest {
|
||||
target: EntityIdentifier;
|
||||
part: EntityBlobSelector & { type: string; size?: number | null };
|
||||
variant?: string;
|
||||
}
|
||||
|
||||
export interface EntityBlobsRequest {
|
||||
target: EntityIdentifier;
|
||||
parts: EntityBlobSelector[];
|
||||
@@ -252,4 +246,4 @@ export interface EntityBlobsResult extends Omit<EntityBlobsWireResult, 'bytes'>
|
||||
blob: Blob;
|
||||
}
|
||||
|
||||
export interface EntityBlobsResponse extends Array<EntityBlobsResult> {}
|
||||
export interface EntityBlobsResponse extends Array<EntityBlobsResult> {}
|
||||
Reference in New Issue
Block a user