generated from Nodarx/template
feat: implement provider
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
42
README.md
42
README.md
@@ -1,42 +0,0 @@
|
||||
# IMAP Mail Provider Module
|
||||
|
||||
This module provides an implementation of an IMAP mail provider using the `gricob/imap` library. It is designed to facilitate email operations such as managing mailboxes and messages through the IMAP protocol.
|
||||
|
||||
## Features
|
||||
|
||||
- **Service Location**: Configures connection details including host, port, and encryption type.
|
||||
- **Service Identity**: Manages user credentials securely.
|
||||
- **Mailbox Management**: Supports operations for listing, fetching, creating, modifying, and deleting mailboxes and messages.
|
||||
- **Autodiscovery**: Implements methods for discovering IMAP services automatically.
|
||||
|
||||
## Installation
|
||||
|
||||
To install the module, run the following command in the module directory:
|
||||
|
||||
```bash
|
||||
composer install
|
||||
```
|
||||
|
||||
This will install the required dependencies, including `gricob/imap`.
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Service Test**: Use the `serviceTest()` method to check connectivity to the IMAP server.
|
||||
2. **Discover Services**: Call `serviceDiscover()` to find available mail services.
|
||||
3. **Mailbox Operations**: Utilize the `RemoteMailService` to perform operations such as listing mailboxes and managing messages.
|
||||
|
||||
## Example
|
||||
|
||||
```php
|
||||
$provider = new KTXM\ProviderImapMail\Providers\Provider();
|
||||
$provider->serviceTest();
|
||||
$mailboxes = $provider->serviceDiscover();
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please submit a pull request or open an issue for any enhancements or bug fixes.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. See the LICENSE file for details.
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "ktxm/provider-imap-mail",
|
||||
"name": "ktxm/provider-imap",
|
||||
"description": "IMAP Mail Provider Module",
|
||||
"type": "library",
|
||||
"minimum-stability": "stable",
|
||||
@@ -9,7 +9,7 @@
|
||||
"platform": {
|
||||
"php": "8.2"
|
||||
},
|
||||
"autoloader-suffix": "ProviderImapMail",
|
||||
"autoloader-suffix": "ProviderImap",
|
||||
"vendor-dir": "lib/vendor",
|
||||
"allow-plugins": {
|
||||
"bamarni/composer-bin-plugin": true
|
||||
@@ -27,13 +27,13 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"KTXM\\ProviderImapMail\\": "lib/",
|
||||
"KTXM\\ProviderImap\\": "lib/",
|
||||
"Gricob\\IMAP\\": "lib/Client"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"KTXT\\ProviderImapMail\\": "tests/php/"
|
||||
"KTXM\\ProviderImap\\": "tests/php/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -35,8 +35,8 @@ final readonly class TraceableResponseStream implements ResponseStream
|
||||
|
||||
private function debug(string $data): void
|
||||
{
|
||||
$data = addslashes($data);
|
||||
$data = str_replace("\r\n", "\\r\\n", $data);
|
||||
// $data = addslashes($data);
|
||||
// $data = str_replace("\r\n", "\\r\\n", $data);
|
||||
|
||||
$this->logger->debug($data);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Console;
|
||||
namespace KTXM\ProviderImap\Console;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\Provider;
|
||||
use KTXM\ProviderImapMail\Providers\Service;
|
||||
use KTXM\ProviderImapMail\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImapMail\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImap\Providers\Provider;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImap\Providers\ServiceLocation;
|
||||
use KTXC\SessionTenant;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
@@ -7,10 +7,10 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Console;
|
||||
namespace KTXM\ProviderImap\Console;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\Provider;
|
||||
use KTXM\ProviderImapMail\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\Provider;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXC\SessionTenant;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
@@ -7,14 +7,14 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Console;
|
||||
namespace KTXM\ProviderImap\Console;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\Provider;
|
||||
use KTXM\ProviderImapMail\Providers\Service;
|
||||
use KTXM\ProviderImapMail\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImapMail\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImapMail\Service\Discovery;
|
||||
use KTXM\ProviderImapMail\Service\Remote\RemoteService;
|
||||
use KTXM\ProviderImap\Providers\Provider;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImap\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImap\Service\Discovery;
|
||||
use KTXM\ProviderImap\Service\Remote\RemoteService;
|
||||
use KTXC\SessionTenant;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
@@ -7,11 +7,11 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Console;
|
||||
namespace KTXM\ProviderImap\Console;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\Provider;
|
||||
use KTXM\ProviderImapMail\Providers\Service;
|
||||
use KTXM\ProviderImapMail\Service\Remote\RemoteService;
|
||||
use KTXM\ProviderImap\Providers\Provider;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
use KTXM\ProviderImap\Service\Remote\RemoteService;
|
||||
use KTXC\SessionTenant;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
@@ -7,18 +7,18 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail;
|
||||
namespace KTXM\ProviderImap;
|
||||
|
||||
use KTXC\Resource\ProviderManager;
|
||||
use KTXF\Module\ModuleBrowserInterface;
|
||||
use KTXF\Module\ModuleConsoleInterface;
|
||||
use KTXF\Module\ModuleInstanceAbstract;
|
||||
use KTXF\Resource\Provider\ProviderInterface;
|
||||
use KTXM\ProviderImapMail\Console\ServiceConnectCommand;
|
||||
use KTXM\ProviderImapMail\Console\ServiceDiscoverCommand;
|
||||
use KTXM\ProviderImapMail\Console\ServiceDisconnectCommand;
|
||||
use KTXM\ProviderImapMail\Console\ServiceTestCommand;
|
||||
use KTXM\ProviderImapMail\Providers\Provider as MailProvider;
|
||||
use KTXM\ProviderImap\Console\ServiceConnectCommand;
|
||||
use KTXM\ProviderImap\Console\ServiceDiscoverCommand;
|
||||
use KTXM\ProviderImap\Console\ServiceDisconnectCommand;
|
||||
use KTXM\ProviderImap\Console\ServiceTestCommand;
|
||||
use KTXM\ProviderImap\Providers\Provider as MailProvider;
|
||||
|
||||
/**
|
||||
* IMAP Mail Provider Module
|
||||
@@ -33,7 +33,7 @@ class Module extends ModuleInstanceAbstract implements ModuleConsoleInterface, M
|
||||
|
||||
public function handle(): string
|
||||
{
|
||||
return 'provider_imap_mail';
|
||||
return 'provider_imap';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
@@ -59,7 +59,7 @@ class Module extends ModuleInstanceAbstract implements ModuleConsoleInterface, M
|
||||
public function permissions(): array
|
||||
{
|
||||
return [
|
||||
'provider_imap_mail' => [
|
||||
'provider_imap' => [
|
||||
'label' => 'Access IMAP Mail Provider',
|
||||
'description' => 'View and access the IMAP mail provider module',
|
||||
'group' => 'Providers',
|
||||
@@ -86,7 +86,7 @@ class Module extends ModuleInstanceAbstract implements ModuleConsoleInterface, M
|
||||
{
|
||||
return [
|
||||
'handle' => $this->handle(),
|
||||
'namespace' => 'ProviderImapMail',
|
||||
'namespace' => 'ProviderImap',
|
||||
'version' => $this->version(),
|
||||
'label' => $this->label(),
|
||||
'author' => $this->author(),
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use Gricob\IMAP\Mailbox;
|
||||
use KTXF\Mail\Collection\CollectionPropertiesMutableAbstract;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use Gricob\IMAP\Mailbox;
|
||||
use KTXF\Mail\Collection\CollectionMutableAbstract;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Gricob\IMAP\Mime\Part\Part;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
/**
|
||||
* Mail Attachment Object
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use Gricob\IMAP\Protocol\Response\Line\Data\Fetch\BodyStructure\MultiPart;
|
||||
use Gricob\IMAP\Protocol\Response\Line\Data\Fetch\BodyStructure\Part;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use DateTimeInterface;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use KTXF\Mail\Provider\ProviderBaseInterface;
|
||||
use KTXF\Mail\Provider\ProviderServiceDiscoverInterface;
|
||||
@@ -16,9 +16,9 @@ use KTXF\Mail\Provider\ProviderServiceTestInterface;
|
||||
use KTXF\Mail\Service\ServiceBaseInterface;
|
||||
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
|
||||
use KTXF\Resource\Provider\ResourceServiceMutateInterface;
|
||||
use KTXM\ProviderImapMail\Service\Discovery;
|
||||
use KTXM\ProviderImapMail\Service\Remote\RemoteService;
|
||||
use KTXM\ProviderImapMail\Stores\ServiceStore;
|
||||
use KTXM\ProviderImap\Service\Discovery;
|
||||
use KTXM\ProviderImap\Service\Remote\RemoteService;
|
||||
use KTXM\ProviderImap\Stores\ServiceStore;
|
||||
|
||||
/**
|
||||
* IMAP Mail Provider
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use Generator;
|
||||
use KTXF\Mail\Collection\CollectionBaseInterface;
|
||||
@@ -29,10 +29,10 @@ use KTXF\Resource\Range\RangeTally;
|
||||
use KTXF\Resource\Range\RangeType;
|
||||
use KTXF\Resource\Sort\ISort;
|
||||
use KTXF\Resource\Sort\Sort;
|
||||
use KTXM\ProviderImapMail\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImapMail\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImapMail\Service\Remote\RemoteMailService;
|
||||
use KTXM\ProviderImapMail\Service\Remote\RemoteService;
|
||||
use KTXM\ProviderImap\Providers\ServiceIdentityBasic;
|
||||
use KTXM\ProviderImap\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImap\Service\Remote\RemoteMailService;
|
||||
use KTXM\ProviderImap\Service\Remote\RemoteService;
|
||||
|
||||
/**
|
||||
* IMAP Mail Service
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use KTXF\Resource\Provider\ResourceServiceIdentityBasic;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
use Gricob\IMAP\Configuration;
|
||||
use KTXF\Resource\Provider\ResourceServiceLocationInterface;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Providers;
|
||||
namespace KTXM\ProviderImap\Providers;
|
||||
|
||||
enum ServiceMode: string
|
||||
{
|
||||
|
||||
@@ -7,11 +7,11 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Service\Cache;
|
||||
namespace KTXM\ProviderImap\Service\Cache;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\CollectionResource;
|
||||
use KTXM\ProviderImapMail\Providers\EntityResource;
|
||||
use KTXM\ProviderImapMail\Stores\MessageStore;
|
||||
use KTXM\ProviderImap\Providers\CollectionResource;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
use KTXM\ProviderImap\Stores\MessageStore;
|
||||
|
||||
/**
|
||||
* Cache Mail Service
|
||||
|
||||
@@ -7,12 +7,12 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Service\Cache;
|
||||
namespace KTXM\ProviderImap\Service\Cache;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\CollectionResource;
|
||||
use KTXM\ProviderImapMail\Providers\EntityResource;
|
||||
use KTXM\ProviderImapMail\Service\Remote\RemoteMailService;
|
||||
use KTXM\ProviderImapMail\Stores\MessageStore;
|
||||
use KTXM\ProviderImap\Providers\CollectionResource;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
use KTXM\ProviderImap\Service\Remote\RemoteMailService;
|
||||
use KTXM\ProviderImap\Stores\MessageStore;
|
||||
|
||||
/**
|
||||
* Cache Service — IMAP Sync Orchestrator
|
||||
|
||||
@@ -7,9 +7,9 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Service;
|
||||
namespace KTXM\ProviderImap\Service;
|
||||
|
||||
use KTXM\ProviderImapMail\Providers\ServiceLocation;
|
||||
use KTXM\ProviderImap\Providers\ServiceLocation;
|
||||
|
||||
/**
|
||||
* IMAP Service Discovery
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Service\Remote;
|
||||
namespace KTXM\ProviderImap\Service\Remote;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Generator;
|
||||
@@ -28,8 +28,8 @@ use KTXF\Resource\Filter\IFilter;
|
||||
use KTXF\Resource\Range\IRange;
|
||||
use KTXF\Resource\Range\RangeAnchorType;
|
||||
use KTXF\Resource\Range\RangeTally;
|
||||
use KTXM\ProviderImapMail\Providers\CollectionResource;
|
||||
use KTXM\ProviderImapMail\Providers\EntityResource;
|
||||
use KTXM\ProviderImap\Providers\CollectionResource;
|
||||
use KTXM\ProviderImap\Providers\EntityResource;
|
||||
|
||||
/**
|
||||
* IMAP Remote Mail Service
|
||||
|
||||
@@ -7,12 +7,12 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Service\Remote;
|
||||
namespace KTXM\ProviderImap\Service\Remote;
|
||||
|
||||
use Gricob\IMAP\Client;
|
||||
use KTXC\Server;
|
||||
use KTXC\Logger\PlainFileLogger;
|
||||
use KTXM\ProviderImapMail\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
|
||||
/**
|
||||
* Static factory for IMAP remote service objects.
|
||||
|
||||
@@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Stores;
|
||||
namespace KTXM\ProviderImap\Stores;
|
||||
|
||||
use KTXC\Db\DataStore;
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ declare(strict_types=1);
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace KTXM\ProviderImapMail\Stores;
|
||||
namespace KTXM\ProviderImap\Stores;
|
||||
|
||||
use KTXC\Db\DataStore;
|
||||
use KTXF\Security\Crypto;
|
||||
use KTXF\Utile\UUID;
|
||||
use KTXM\ProviderImapMail\Providers\Service;
|
||||
use KTXM\ProviderImap\Providers\Service;
|
||||
|
||||
/**
|
||||
* IMAP Service Store
|
||||
|
||||
1599
package-lock.json
generated
Normal file
1599
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "provider_imap",
|
||||
"description": "Ktrix IMAP Provider Module",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Ktrix",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build --mode production --config vite.config.ts",
|
||||
"dev": "vite build --mode development --config vite.config.ts",
|
||||
"watch": "vite build --mode development --watch --config vite.config.ts",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^2.3.1",
|
||||
"vue": "^3.5.18",
|
||||
"vue-router": "^4.5.1",
|
||||
"vuetify": "^3.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.1.2",
|
||||
"vue-tsc": "^3.0.5"
|
||||
}
|
||||
}
|
||||
131
src/components/ImapAuthPanel.vue
Normal file
131
src/components/ImapAuthPanel.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import type { ServiceIdentity } from '@KTXM/MailManager/types/service'
|
||||
import type { ProviderAuthPanelProps, ProviderAuthPanelEmits } from '@KTXM/MailManager/types/integration'
|
||||
|
||||
const props = defineProps<ProviderAuthPanelProps>()
|
||||
const emit = defineEmits<ProviderAuthPanelEmits>()
|
||||
|
||||
const identity = ref(props.prefilledIdentity || props.emailAddress || '')
|
||||
const secret = ref(props.prefilledSecret || '')
|
||||
|
||||
const rules = {
|
||||
required: (value: unknown) => !!value || 'This field is required'
|
||||
}
|
||||
|
||||
const isValid = computed(() => !!identity.value && !!secret.value)
|
||||
|
||||
const currentIdentity = computed((): ServiceIdentity | null => {
|
||||
if (!isValid.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'BA',
|
||||
identity: identity.value,
|
||||
secret: secret.value,
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
currentIdentity,
|
||||
value => {
|
||||
if (value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
isValid,
|
||||
value => {
|
||||
emit('valid', value)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
value => {
|
||||
if (value?.type === 'BA') {
|
||||
identity.value = value.identity || ''
|
||||
secret.value = value.secret || ''
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.emailAddress,
|
||||
value => {
|
||||
if (value && !identity.value) {
|
||||
identity.value = value
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="imap-auth-panel">
|
||||
<h3 class="text-h6 mb-4">Authentication</h3>
|
||||
<p class="text-body-2 mb-6">Provide the username and password your IMAP server expects.</p>
|
||||
|
||||
<v-alert type="info" variant="tonal" class="mb-4">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-information</v-icon>
|
||||
</template>
|
||||
<div class="text-caption">
|
||||
Most IMAP servers use your full email address as the username. Use an app password if your mail host requires one.
|
||||
</div>
|
||||
</v-alert>
|
||||
|
||||
<v-text-field
|
||||
v-model="identity"
|
||||
label="Username / Email"
|
||||
hint="Account login used by the IMAP server"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-account"
|
||||
class="mb-4"
|
||||
autocomplete="username"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="secret"
|
||||
type="password"
|
||||
label="Password"
|
||||
hint="Password or app-specific password"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-lock"
|
||||
class="mb-4"
|
||||
autocomplete="current-password"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.imap-auth-panel {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.text-h6 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
line-height: 2rem;
|
||||
letter-spacing: 0.0125em;
|
||||
}
|
||||
|
||||
.text-body-2 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.25rem;
|
||||
letter-spacing: 0.0178571429em;
|
||||
color: rgba(var(--v-theme-on-surface), 0.7);
|
||||
}
|
||||
</style>
|
||||
277
src/components/ImapConfigPanel.vue
Normal file
277
src/components/ImapConfigPanel.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import type {
|
||||
ServiceLocation,
|
||||
ServiceLocationSocketSole,
|
||||
ServiceLocationUri,
|
||||
} from '@KTXM/MailManager/types/service'
|
||||
import type { ProviderConfigPanelProps, ProviderConfigPanelEmits } from '@KTXM/MailManager/types/integration'
|
||||
|
||||
type ImapEncryption = 'none' | 'ssl' | 'tls' | 'starttls'
|
||||
|
||||
type ImapLocation = ServiceLocationSocketSole & {
|
||||
type: 'SOCKET_SOLE' | 'URI'
|
||||
verifyPeerName?: boolean
|
||||
allowSelfSigned?: boolean
|
||||
}
|
||||
|
||||
const props = defineProps<ProviderConfigPanelProps>()
|
||||
const emit = defineEmits<ProviderConfigPanelEmits>()
|
||||
|
||||
function asImapLocation(location?: ServiceLocation): ImapLocation | null {
|
||||
if (!location) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (location.type === 'SOCKET_SOLE') {
|
||||
return location as ImapLocation
|
||||
}
|
||||
|
||||
if (location.type === 'URI') {
|
||||
const uriLocation = location as ServiceLocationUri & {
|
||||
encryption?: ImapEncryption
|
||||
verifyPeerName?: boolean
|
||||
allowSelfSigned?: boolean
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'URI',
|
||||
host: uriLocation.host || '',
|
||||
port: uriLocation.port || 993,
|
||||
encryption: uriLocation.encryption || 'ssl',
|
||||
verifyPeer: uriLocation.verifyPeer ?? true,
|
||||
verifyHost: uriLocation.verifyPeerName ?? uriLocation.verifyHost ?? true,
|
||||
verifyPeerName: uriLocation.verifyPeerName ?? uriLocation.verifyHost ?? true,
|
||||
allowSelfSigned: uriLocation.allowSelfSigned ?? false,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function defaultPortFor(encryption: ImapEncryption): number {
|
||||
return encryption === 'ssl' || encryption === 'tls' ? 993 : 143
|
||||
}
|
||||
|
||||
const sourceLocation = computed(() => asImapLocation(props.modelValue || props.discoveredLocation))
|
||||
|
||||
const host = ref(sourceLocation.value?.host || '')
|
||||
const encryption = ref<ImapEncryption>(sourceLocation.value?.encryption || 'ssl')
|
||||
const port = ref(String(sourceLocation.value?.port || defaultPortFor(encryption.value)))
|
||||
const verifyPeer = ref(sourceLocation.value?.verifyPeer ?? true)
|
||||
const verifyHost = ref(sourceLocation.value?.verifyPeerName ?? sourceLocation.value?.verifyHost ?? true)
|
||||
const allowSelfSigned = ref(sourceLocation.value?.allowSelfSigned ?? false)
|
||||
|
||||
const encryptionOptions = [
|
||||
{ title: 'Implicit TLS (SSL)', value: 'ssl' },
|
||||
{ title: 'TLS', value: 'tls' },
|
||||
{ title: 'STARTTLS', value: 'starttls' },
|
||||
{ title: 'None', value: 'none' },
|
||||
]
|
||||
|
||||
const rules = {
|
||||
required: (value: unknown) => !!value || 'This field is required',
|
||||
port: (value: string) => {
|
||||
const numericValue = Number(value)
|
||||
return Number.isInteger(numericValue) && numericValue >= 1 && numericValue <= 65535
|
||||
? true
|
||||
: 'Port must be between 1 and 65535'
|
||||
}
|
||||
}
|
||||
|
||||
const isValid = computed(() => !!host.value && rules.port(port.value) === true)
|
||||
|
||||
const currentLocation = computed((): ServiceLocation | null => {
|
||||
if (!isValid.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
const numericPort = Number(port.value)
|
||||
const location: ImapLocation = {
|
||||
type: 'SOCKET_SOLE',
|
||||
host: host.value,
|
||||
port: numericPort,
|
||||
encryption: encryption.value,
|
||||
verifyPeer: verifyPeer.value,
|
||||
verifyHost: verifyHost.value,
|
||||
verifyPeerName: verifyHost.value,
|
||||
allowSelfSigned: allowSelfSigned.value,
|
||||
}
|
||||
|
||||
return location as ServiceLocation
|
||||
})
|
||||
|
||||
watch(
|
||||
currentLocation,
|
||||
value => {
|
||||
if (value) {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
isValid,
|
||||
value => {
|
||||
emit('valid', value)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
value => {
|
||||
const next = asImapLocation(value)
|
||||
if (!next) {
|
||||
return
|
||||
}
|
||||
|
||||
host.value = next.host || ''
|
||||
encryption.value = next.encryption || 'ssl'
|
||||
port.value = String(next.port || defaultPortFor(encryption.value))
|
||||
verifyPeer.value = next.verifyPeer ?? true
|
||||
verifyHost.value = next.verifyPeerName ?? next.verifyHost ?? true
|
||||
allowSelfSigned.value = next.allowSelfSigned ?? false
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.discoveredLocation,
|
||||
value => {
|
||||
if (props.modelValue) {
|
||||
return
|
||||
}
|
||||
|
||||
const next = asImapLocation(value)
|
||||
if (!next) {
|
||||
return
|
||||
}
|
||||
|
||||
host.value = next.host || ''
|
||||
encryption.value = next.encryption || 'ssl'
|
||||
port.value = String(next.port || defaultPortFor(encryption.value))
|
||||
verifyPeer.value = next.verifyPeer ?? true
|
||||
verifyHost.value = next.verifyPeerName ?? next.verifyHost ?? true
|
||||
allowSelfSigned.value = next.allowSelfSigned ?? false
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(encryption, (next, previous) => {
|
||||
const previousDefault = defaultPortFor(previous)
|
||||
if (!port.value || Number(port.value) === previousDefault) {
|
||||
port.value = String(defaultPortFor(next))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="imap-config-panel">
|
||||
<h3 class="text-h6 mb-4">IMAP Connection Settings</h3>
|
||||
<p class="text-body-2 mb-6">Configure the server address, transport security, and certificate verification for your IMAP mailbox.</p>
|
||||
|
||||
<v-text-field
|
||||
v-model="host"
|
||||
label="Server Host"
|
||||
hint="For example: imap.example.com"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-server"
|
||||
class="mb-4"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
|
||||
<v-select
|
||||
v-model="encryption"
|
||||
:items="encryptionOptions"
|
||||
label="Security"
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-shield-lock"
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="port"
|
||||
label="Port"
|
||||
hint="Defaults to 993 for TLS/SSL and 143 for plain or STARTTLS"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-numeric"
|
||||
class="mb-4"
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
:rules="[rules.required, rules.port]"
|
||||
/>
|
||||
|
||||
<v-expansion-panels class="mt-4">
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
<v-icon start>mdi-cog</v-icon>
|
||||
Security Options
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-switch
|
||||
v-model="verifyPeer"
|
||||
label="Verify TLS certificate"
|
||||
color="primary"
|
||||
hint="Disable only for trusted internal or test environments"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="verifyHost"
|
||||
label="Verify certificate hostname"
|
||||
color="primary"
|
||||
hint="Checks that the certificate matches the IMAP host"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="allowSelfSigned"
|
||||
label="Allow self-signed certificates"
|
||||
color="primary"
|
||||
hint="Use only when your server is intentionally deployed with a self-signed certificate"
|
||||
persistent-hint
|
||||
/>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
|
||||
<v-alert type="info" variant="tonal" density="compact" class="mt-4">
|
||||
<template #prepend>
|
||||
<v-icon>mdi-information</v-icon>
|
||||
</template>
|
||||
<div class="text-caption">
|
||||
STARTTLS is accepted for compatibility, but the current IMAP client transport does not perform STARTTLS negotiation. Prefer TLS on port 993 when available.
|
||||
</div>
|
||||
</v-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.imap-config-panel {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.text-h6 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
line-height: 2rem;
|
||||
letter-spacing: 0.0125em;
|
||||
}
|
||||
|
||||
.text-body-2 {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.25rem;
|
||||
letter-spacing: 0.0178571429em;
|
||||
color: rgba(var(--v-theme-on-surface), 0.7);
|
||||
}
|
||||
</style>
|
||||
38
src/integrations.ts
Normal file
38
src/integrations.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { ModuleIntegrations } from '@KTXC/types/moduleTypes'
|
||||
import type { ServiceInterface } from '@KTXM/MailManager/types/service'
|
||||
import { ServiceObject } from '@KTXM/MailManager/models/service'
|
||||
|
||||
const integrations: ModuleIntegrations = {
|
||||
mail_account_config_panels: [
|
||||
{
|
||||
id: 'imap',
|
||||
label: 'IMAP',
|
||||
icon: 'mdi-email',
|
||||
caption: 'Internet Message Access Protocol',
|
||||
component: () => import('@/components/ImapConfigPanel.vue'),
|
||||
priority: 20,
|
||||
}
|
||||
],
|
||||
mail_account_auth_panels: [
|
||||
{
|
||||
id: 'imap',
|
||||
component: () => import('@/components/ImapAuthPanel.vue'),
|
||||
}
|
||||
],
|
||||
mail_service_factory: [
|
||||
{
|
||||
id: 'imap',
|
||||
factory: (data: ServiceInterface) => new ServiceObject().fromJson(data)
|
||||
}
|
||||
],
|
||||
mail_provider_metadata: [
|
||||
{
|
||||
id: 'imap',
|
||||
label: 'IMAP',
|
||||
description: 'Classic mailbox access over IMAP',
|
||||
icon: 'mdi-email',
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default integrations
|
||||
12
src/main.ts
Normal file
12
src/main.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import routes from '@/routes'
|
||||
import integrations from '@/integrations'
|
||||
import type { App as VueApp } from 'vue'
|
||||
|
||||
export const css = ['__CSS_FILENAME_PLACEHOLDER__']
|
||||
|
||||
export { routes, integrations }
|
||||
|
||||
export default {
|
||||
install(_app: VueApp) {
|
||||
}
|
||||
}
|
||||
3
src/routes.ts
Normal file
3
src/routes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const routes = []
|
||||
|
||||
export default routes
|
||||
1
src/style.css
Normal file
1
src/style.css
Normal file
@@ -0,0 +1 @@
|
||||
/* imap provider module styles */
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
18
tsconfig.app.json
Normal file
18
tsconfig.app.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@KTXC/*": ["../../core/src/*"],
|
||||
"@KTXM/MailManager/*": ["../mail_manager/src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
21
tsconfig.node.json
Normal file
21
tsconfig.node.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
59
vite.config.ts
Normal file
59
vite.config.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
{
|
||||
name: 'inject-css-filename',
|
||||
enforce: 'post',
|
||||
generateBundle(_options, bundle) {
|
||||
const cssFile = Object.keys(bundle).find(name => name.endsWith('.css'))
|
||||
if (!cssFile) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const fileName of Object.keys(bundle)) {
|
||||
const chunk = bundle[fileName]
|
||||
if (chunk.type === 'chunk' && chunk.code.includes('__CSS_FILENAME_PLACEHOLDER__')) {
|
||||
chunk.code = chunk.code.replace(/__CSS_FILENAME_PLACEHOLDER__/g, `static/${cssFile}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
'@KTXC': path.resolve(__dirname, '../../core/src'),
|
||||
'@KTXM/MailManager': path.resolve(__dirname, '../mail_manager/src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: 'static',
|
||||
emptyOutDir: true,
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, 'src/main.ts'),
|
||||
formats: ['es'],
|
||||
fileName: () => 'module.mjs',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
'vue',
|
||||
'vue-router',
|
||||
'pinia',
|
||||
],
|
||||
output: {
|
||||
assetFileNames: assetInfo => {
|
||||
if (assetInfo.name?.endsWith('.css')) {
|
||||
return 'provider_imap-[hash].css'
|
||||
}
|
||||
|
||||
return '[name]-[hash][extname]'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user