mirror of
https://github.com/LogicLabs-OU/OpenArchiver.git
synced 2026-04-06 00:31:57 +02:00
Microsoft 365 sync
This commit is contained in:
@@ -5,6 +5,7 @@ import { EmailProviderFactory } from '../../services/EmailProviderFactory';
|
||||
import { GoogleWorkspaceConnector } from '../../services/ingestion-connectors/GoogleWorkspaceConnector';
|
||||
import { flowProducer, ingestionQueue } from '../queues';
|
||||
import { logger } from '../../config/logger';
|
||||
import { MicrosoftConnector } from '../../services/ingestion-connectors/MicrosoftConnector';
|
||||
|
||||
export default async (job: Job<IInitialImportJob>) => {
|
||||
const { ingestionSourceId } = job.data;
|
||||
@@ -23,7 +24,7 @@ export default async (job: Job<IInitialImportJob>) => {
|
||||
|
||||
const connector = EmailProviderFactory.createConnector(source);
|
||||
|
||||
if (connector instanceof GoogleWorkspaceConnector) {
|
||||
if (connector instanceof GoogleWorkspaceConnector || connector instanceof MicrosoftConnector) {
|
||||
const jobs = [];
|
||||
let userCount = 0;
|
||||
for await (const user of connector.listAllUsers()) {
|
||||
|
||||
@@ -4,7 +4,8 @@ import type {
|
||||
Microsoft365Credentials,
|
||||
GenericImapCredentials,
|
||||
EmailObject,
|
||||
SyncState
|
||||
SyncState,
|
||||
MailboxUser
|
||||
} from '@open-archiver/types';
|
||||
import { GoogleWorkspaceConnector } from './ingestion-connectors/GoogleWorkspaceConnector';
|
||||
import { MicrosoftConnector } from './ingestion-connectors/MicrosoftConnector';
|
||||
@@ -15,7 +16,7 @@ export interface IEmailConnector {
|
||||
testConnection(): Promise<boolean>;
|
||||
fetchEmails(userEmail: string, syncState?: SyncState | null): AsyncGenerator<EmailObject | null>;
|
||||
getUpdatedSyncState(userEmail?: string): SyncState;
|
||||
listAllUsers?(): AsyncGenerator<any>;
|
||||
listAllUsers?(): AsyncGenerator<MailboxUser>;
|
||||
returnImapUserEmail?(): string;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ import type {
|
||||
GoogleWorkspaceCredentials,
|
||||
EmailObject,
|
||||
EmailAddress,
|
||||
SyncState
|
||||
SyncState,
|
||||
MailboxUser
|
||||
} from '@open-archiver/types';
|
||||
import type { IEmailConnector } from '../EmailProviderFactory';
|
||||
import { logger } from '../../config/logger';
|
||||
@@ -88,7 +89,7 @@ export class GoogleWorkspaceConnector implements IEmailConnector {
|
||||
* This method handles pagination to retrieve the complete list of users.
|
||||
* @returns An async generator that yields each user object.
|
||||
*/
|
||||
public async *listAllUsers(): AsyncGenerator<admin_directory_v1.Schema$User> {
|
||||
public async *listAllUsers(): AsyncGenerator<MailboxUser> {
|
||||
const authClient = this.getAuthClient(this.credentials.impersonatedAdminEmail, [
|
||||
'https://www.googleapis.com/auth/admin.directory.user.readonly'
|
||||
]);
|
||||
@@ -107,7 +108,13 @@ export class GoogleWorkspaceConnector implements IEmailConnector {
|
||||
const users = res.data.users;
|
||||
if (users) {
|
||||
for (const user of users) {
|
||||
yield user;
|
||||
if (user.id && user.primaryEmail && user.name?.fullName) {
|
||||
yield {
|
||||
id: user.id,
|
||||
primaryEmail: user.primaryEmail,
|
||||
displayName: user.name.fullName
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
pageToken = res.data.nextPageToken ?? undefined;
|
||||
|
||||
@@ -3,7 +3,8 @@ import type {
|
||||
Microsoft365Credentials,
|
||||
EmailObject,
|
||||
EmailAddress,
|
||||
SyncState
|
||||
SyncState,
|
||||
MailboxUser
|
||||
} from '@open-archiver/types';
|
||||
import type { IEmailConnector } from '../EmailProviderFactory';
|
||||
import { logger } from '../../config/logger';
|
||||
@@ -96,14 +97,20 @@ export class MicrosoftConnector implements IEmailConnector {
|
||||
* This method handles pagination to retrieve the complete list of users.
|
||||
* @returns An async generator that yields each user object.
|
||||
*/
|
||||
public async *listAllUsers(): AsyncGenerator<User> {
|
||||
public async *listAllUsers(): AsyncGenerator<MailboxUser> {
|
||||
let request = this.graphClient.api('/users').select('id,userPrincipalName,displayName');
|
||||
|
||||
try {
|
||||
let response = await request.get();
|
||||
while (response) {
|
||||
for (const user of response.value) {
|
||||
yield user;
|
||||
for (const user of response.value as User[]) {
|
||||
if (user.id && user.userPrincipalName && user.displayName) {
|
||||
yield {
|
||||
id: user.id,
|
||||
primaryEmail: user.userPrincipalName,
|
||||
displayName: user.displayName
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (response['@odata.nextLink']) {
|
||||
@@ -131,7 +138,7 @@ export class MicrosoftConnector implements IEmailConnector {
|
||||
const deltaToken = syncState?.microsoft?.[userEmail]?.deltaToken;
|
||||
let requestUrl = deltaToken
|
||||
? deltaToken
|
||||
: `/users/${userEmail}/messages/delta`;
|
||||
: `/users/${userEmail}/mailFolders/AllItems/messages/delta`;
|
||||
|
||||
try {
|
||||
while (requestUrl) {
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
raw,
|
||||
rawHtml
|
||||
}: { raw?: Buffer | { type: 'Buffer'; data: number[] } | undefined; rawHtml?: string } = $props();
|
||||
|
||||
let parsedEmail: Email | null = $state(null);
|
||||
let isLoading = $state(true);
|
||||
|
||||
// By adding a <base> tag, all relative and absolute links in the HTML document
|
||||
// will open in a new tab by default.
|
||||
let emailHtml = $derived(() => {
|
||||
if (parsedEmail && parsedEmail?.html) {
|
||||
if (parsedEmail && parsedEmail.html) {
|
||||
return `<base target="_blank" />${parsedEmail.html}`;
|
||||
} else if (parsedEmail && parsedEmail.text) {
|
||||
return `<base target="_blank" />${parsedEmail.text}`;
|
||||
} else if (rawHtml) {
|
||||
return `<base target="_blank" />${rawHtml}`;
|
||||
}
|
||||
@@ -33,6 +34,7 @@
|
||||
}
|
||||
const parsed = await new PostalMime().parse(buffer);
|
||||
parsedEmail = parsed;
|
||||
console.log(parsedEmail);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse email:', error);
|
||||
} finally {
|
||||
|
||||
@@ -90,19 +90,21 @@
|
||||
</div>
|
||||
{:else if formData.provider === 'microsoft_365'}
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="clientId" class="text-right">Client ID</Label>
|
||||
<Label for="clientId" class="text-right">Application (Client) ID</Label>
|
||||
<Input id="clientId" bind:value={formData.providerConfig.clientId} class="col-span-3" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="clientSecret" class="text-right">Client Secret</Label>
|
||||
<Label for="clientSecret" class="text-right">Client Secret Value</Label>
|
||||
<Input
|
||||
id="clientSecret"
|
||||
type="password"
|
||||
placeholder="Enter the secret Value, not the Secret ID"
|
||||
bind:value={formData.providerConfig.clientSecret}
|
||||
class="col-span-3"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label for="tenantId" class="text-right">Tenant ID</Label>
|
||||
<Label for="tenantId" class="text-right">Directory (Tenant) ID</Label>
|
||||
<Input id="tenantId" bind:value={formData.providerConfig.tenantId} class="col-span-3" />
|
||||
</div>
|
||||
{:else if formData.provider === 'generic_imap'}
|
||||
|
||||
@@ -108,3 +108,9 @@ export interface IProcessMailboxJob {
|
||||
ingestionSourceId: string;
|
||||
userEmail: string;
|
||||
}
|
||||
|
||||
export type MailboxUser = {
|
||||
id: string;
|
||||
primaryEmail: string;
|
||||
displayName: string;
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user