@realtimex/email-automator 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +35 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/api/server.ts +130 -0
- package/api/src/config/index.ts +102 -0
- package/api/src/middleware/auth.ts +166 -0
- package/api/src/middleware/errorHandler.ts +97 -0
- package/api/src/middleware/index.ts +4 -0
- package/api/src/middleware/rateLimit.ts +87 -0
- package/api/src/middleware/validation.ts +118 -0
- package/api/src/routes/actions.ts +214 -0
- package/api/src/routes/auth.ts +157 -0
- package/api/src/routes/emails.ts +144 -0
- package/api/src/routes/health.ts +36 -0
- package/api/src/routes/index.ts +22 -0
- package/api/src/routes/migrate.ts +76 -0
- package/api/src/routes/rules.ts +149 -0
- package/api/src/routes/settings.ts +229 -0
- package/api/src/routes/sync.ts +152 -0
- package/api/src/services/eventLogger.ts +52 -0
- package/api/src/services/gmail.ts +456 -0
- package/api/src/services/intelligence.ts +288 -0
- package/api/src/services/microsoft.ts +368 -0
- package/api/src/services/processor.ts +596 -0
- package/api/src/services/scheduler.ts +255 -0
- package/api/src/services/supabase.ts +144 -0
- package/api/src/utils/contentCleaner.ts +114 -0
- package/api/src/utils/crypto.ts +80 -0
- package/api/src/utils/logger.ts +142 -0
- package/bin/email-automator-deploy.js +79 -0
- package/bin/email-automator-setup.js +144 -0
- package/bin/email-automator.js +61 -0
- package/dist/assets/index-BQ1uMdFh.js +97 -0
- package/dist/assets/index-Dzi17fx5.css +1 -0
- package/dist/email-automator-logo.svg +51 -0
- package/dist/favicon.svg +45 -0
- package/dist/index.html +14 -0
- package/index.html +13 -0
- package/package.json +112 -0
- package/public/email-automator-logo.svg +51 -0
- package/public/favicon.svg +45 -0
- package/scripts/deploy-functions.sh +55 -0
- package/scripts/migrate.sh +177 -0
- package/src/App.tsx +622 -0
- package/src/components/AccountSettings.tsx +310 -0
- package/src/components/AccountSettingsPage.tsx +390 -0
- package/src/components/Configuration.tsx +1345 -0
- package/src/components/Dashboard.tsx +940 -0
- package/src/components/ErrorBoundary.tsx +71 -0
- package/src/components/LiveTerminal.tsx +308 -0
- package/src/components/LoadingSpinner.tsx +39 -0
- package/src/components/Login.tsx +371 -0
- package/src/components/Logo.tsx +57 -0
- package/src/components/SetupWizard.tsx +388 -0
- package/src/components/Toast.tsx +109 -0
- package/src/components/migration/MigrationBanner.tsx +97 -0
- package/src/components/migration/MigrationModal.tsx +458 -0
- package/src/components/migration/MigrationPulseIndicator.tsx +38 -0
- package/src/components/mode-toggle.tsx +24 -0
- package/src/components/theme-provider.tsx +72 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/button.tsx +57 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/dialog.tsx +133 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/otp-input.tsx +184 -0
- package/src/context/AppContext.tsx +422 -0
- package/src/context/MigrationContext.tsx +53 -0
- package/src/context/TerminalContext.tsx +31 -0
- package/src/core/actions.ts +76 -0
- package/src/core/auth.ts +108 -0
- package/src/core/intelligence.ts +76 -0
- package/src/core/processor.ts +112 -0
- package/src/hooks/useRealtimeEmails.ts +111 -0
- package/src/index.css +140 -0
- package/src/lib/api-config.ts +42 -0
- package/src/lib/api-old.ts +228 -0
- package/src/lib/api.ts +421 -0
- package/src/lib/migration-check.ts +264 -0
- package/src/lib/sounds.ts +120 -0
- package/src/lib/supabase-config.ts +117 -0
- package/src/lib/supabase.ts +28 -0
- package/src/lib/types.ts +166 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +10 -0
- package/supabase/.env.example +15 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/supabase/config.toml +95 -0
- package/supabase/functions/_shared/auth-helper.ts +76 -0
- package/supabase/functions/_shared/auth.ts +33 -0
- package/supabase/functions/_shared/cors.ts +45 -0
- package/supabase/functions/_shared/encryption.ts +70 -0
- package/supabase/functions/_shared/supabaseAdmin.ts +14 -0
- package/supabase/functions/api-v1-accounts/index.ts +133 -0
- package/supabase/functions/api-v1-emails/index.ts +177 -0
- package/supabase/functions/api-v1-rules/index.ts +177 -0
- package/supabase/functions/api-v1-settings/index.ts +247 -0
- package/supabase/functions/auth-gmail/index.ts +197 -0
- package/supabase/functions/auth-microsoft/index.ts +215 -0
- package/supabase/functions/setup/index.ts +92 -0
- package/supabase/migrations/20260114000000_initial_schema.sql +81 -0
- package/supabase/migrations/20260115000000_add_user_settings.sql +49 -0
- package/supabase/migrations/20260115000001_add_auth_flow.sql +80 -0
- package/supabase/migrations/20260115000002_fix_permissions.sql +5 -0
- package/supabase/migrations/20260115000003_fix_init_state_permissions.sql +9 -0
- package/supabase/migrations/20260115000004_add_migration_rpc.sql +13 -0
- package/supabase/migrations/20260115000005_add_provider_creds.sql +7 -0
- package/supabase/migrations/20260115000006_backfill_profiles.sql +22 -0
- package/supabase/migrations/20260116000000_add_sync_scope.sql +15 -0
- package/supabase/migrations/20260116000001_per_account_sync_scope.sql +19 -0
- package/supabase/migrations/20260116000002_add_llm_api_key.sql +5 -0
- package/supabase/migrations/20260117000000_refactor_integrations.sql +36 -0
- package/supabase/migrations/20260117000001_add_processing_events.sql +30 -0
- package/supabase/migrations/20260117000002_multi_actions.sql +15 -0
- package/supabase/migrations/20260117000003_seed_default_rules.sql +77 -0
- package/supabase/migrations/20260117000004_rule_instructions.sql +5 -0
- package/supabase/migrations/20260117000005_rule_attachments.sql +7 -0
- package/supabase/migrations/20260117000006_setup_storage.sql +32 -0
- package/supabase/migrations/20260117000007_add_system_logs.sql +26 -0
- package/supabase/migrations/20260117000008_link_logs_to_accounts.sql +8 -0
- package/supabase/migrations/20260117000009_convert_toggles_to_rules.sql +28 -0
- package/supabase/migrations/20260117000010_add_atomic_action_append.sql +13 -0
- package/supabase/migrations/20260117000011_add_profile_avatar.sql +4 -0
- package/supabase/migrations/20260117000012_setup_avatars_storage.sql +26 -0
- package/supabase/templates/confirmation.html +76 -0
- package/supabase/templates/email-change.html +76 -0
- package/supabase/templates/invite.html +72 -0
- package/supabase/templates/magic-link.html +68 -0
- package/supabase/templates/recovery.html +82 -0
- package/tsconfig.json +36 -0
- package/vite.config.ts +162 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { config } from '../config/index.js';
|
|
2
|
+
|
|
3
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
4
|
+
|
|
5
|
+
interface LogMeta {
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LOG_COLORS = {
|
|
10
|
+
debug: '\x1b[36m', // cyan
|
|
11
|
+
info: '\x1b[32m', // green
|
|
12
|
+
warn: '\x1b[33m', // yellow
|
|
13
|
+
error: '\x1b[31m', // red
|
|
14
|
+
reset: '\x1b[0m',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
18
|
+
debug: 0,
|
|
19
|
+
info: 1,
|
|
20
|
+
warn: 2,
|
|
21
|
+
error: 3,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class Logger {
|
|
25
|
+
private minLevel: LogLevel;
|
|
26
|
+
private context?: string;
|
|
27
|
+
private static supabaseClient: any = null;
|
|
28
|
+
private static currentUserId: string | null = null;
|
|
29
|
+
|
|
30
|
+
constructor(context?: string) {
|
|
31
|
+
this.minLevel = config.isProduction ? 'info' : 'debug';
|
|
32
|
+
this.context = context;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the Supabase client and current user ID for DB persistence.
|
|
37
|
+
* This is called by the auth middleware or server initialization.
|
|
38
|
+
*/
|
|
39
|
+
static setPersistence(client: any, userId: string | null = null): void {
|
|
40
|
+
Logger.supabaseClient = client;
|
|
41
|
+
Logger.currentUserId = userId;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private shouldLog(level: LogLevel): boolean {
|
|
45
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[this.minLevel];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async saveToSupabase(level: LogLevel, message: string, meta?: LogMeta): Promise<void> {
|
|
49
|
+
// Only persist warn and error levels to DB to prevent bloat
|
|
50
|
+
// unless explicitly forced via meta
|
|
51
|
+
const persistLevels: LogLevel[] = ['warn', 'error'];
|
|
52
|
+
const shouldPersist = persistLevels.includes(level) || meta?._persist === true;
|
|
53
|
+
|
|
54
|
+
if (shouldPersist && Logger.supabaseClient) {
|
|
55
|
+
try {
|
|
56
|
+
// Remove internal flags from meta before saving
|
|
57
|
+
const { _persist, ...cleanMeta } = meta || {};
|
|
58
|
+
|
|
59
|
+
await Logger.supabaseClient.from('system_logs').insert({
|
|
60
|
+
user_id: Logger.currentUserId,
|
|
61
|
+
level,
|
|
62
|
+
source: this.context || 'System',
|
|
63
|
+
message,
|
|
64
|
+
metadata: cleanMeta,
|
|
65
|
+
created_at: new Date().toISOString()
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
// Fail silently to avoid infinite loops if logging fails
|
|
69
|
+
console.error('[Logger] Failed to persist log to Supabase:', err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private formatMessage(level: LogLevel, message: string, meta?: LogMeta): string {
|
|
75
|
+
const timestamp = new Date().toISOString();
|
|
76
|
+
const contextStr = this.context ? `[${this.context}]` : '';
|
|
77
|
+
const { _persist, ...cleanMeta } = meta || {};
|
|
78
|
+
const metaStr = Object.keys(cleanMeta).length > 0 ? ` ${JSON.stringify(cleanMeta)}` : '';
|
|
79
|
+
|
|
80
|
+
if (config.isProduction) {
|
|
81
|
+
return JSON.stringify({
|
|
82
|
+
timestamp,
|
|
83
|
+
level,
|
|
84
|
+
context: this.context,
|
|
85
|
+
message,
|
|
86
|
+
...cleanMeta,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const color = LOG_COLORS[level];
|
|
91
|
+
const reset = LOG_COLORS.reset;
|
|
92
|
+
return `${timestamp} ${color}${level.toUpperCase().padEnd(5)}${reset} ${contextStr} ${message}${metaStr}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
debug(message: string, meta?: LogMeta): void {
|
|
96
|
+
if (this.shouldLog('debug')) {
|
|
97
|
+
console.debug(this.formatMessage('debug', message, meta));
|
|
98
|
+
this.saveToSupabase('debug', message, meta);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
info(message: string, meta?: LogMeta): void {
|
|
103
|
+
if (this.shouldLog('info')) {
|
|
104
|
+
console.info(this.formatMessage('info', message, meta));
|
|
105
|
+
this.saveToSupabase('info', message, meta);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
warn(message: string, meta?: LogMeta): void {
|
|
110
|
+
if (this.shouldLog('warn')) {
|
|
111
|
+
console.warn(this.formatMessage('warn', message, meta));
|
|
112
|
+
this.saveToSupabase('warn', message, meta);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
error(message: string, error?: Error | unknown, meta?: LogMeta): void {
|
|
117
|
+
if (this.shouldLog('error')) {
|
|
118
|
+
const errorMeta: LogMeta = { ...meta };
|
|
119
|
+
if (error instanceof Error) {
|
|
120
|
+
errorMeta.errorName = error.name;
|
|
121
|
+
errorMeta.errorMessage = error.message;
|
|
122
|
+
errorMeta.stack = error.stack;
|
|
123
|
+
} else if (error) {
|
|
124
|
+
errorMeta.error = error;
|
|
125
|
+
}
|
|
126
|
+
console.error(this.formatMessage('error', message, errorMeta));
|
|
127
|
+
this.saveToSupabase('error', message, errorMeta);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
child(context: string): Logger {
|
|
132
|
+
return new Logger(this.context ? `${this.context}:${context}` : context);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Default logger instance
|
|
137
|
+
export const logger = new Logger();
|
|
138
|
+
|
|
139
|
+
// Factory for creating contextual loggers
|
|
140
|
+
export function createLogger(context: string): Logger {
|
|
141
|
+
return new Logger(context);
|
|
142
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Email Automator Deploy CLI
|
|
5
|
+
* Deploy Edge Functions to Supabase
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
console.log('🚀 Email Automator - Edge Functions Deployment');
|
|
17
|
+
console.log('===============================================');
|
|
18
|
+
console.log('');
|
|
19
|
+
|
|
20
|
+
// Check if supabase CLI is available
|
|
21
|
+
const checkSupabase = spawn('supabase', ['--version'], { stdio: 'pipe' });
|
|
22
|
+
|
|
23
|
+
checkSupabase.on('error', () => {
|
|
24
|
+
console.error('❌ Supabase CLI not found');
|
|
25
|
+
console.error('');
|
|
26
|
+
console.error('Install it with:');
|
|
27
|
+
console.error(' npm install -g supabase');
|
|
28
|
+
console.error('');
|
|
29
|
+
console.error('Or via Homebrew (macOS):');
|
|
30
|
+
console.error(' brew install supabase/tap/supabase');
|
|
31
|
+
console.error('');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
checkSupabase.on('close', (code) => {
|
|
36
|
+
if (code !== 0) {
|
|
37
|
+
console.error('❌ Supabase CLI check failed');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('✅ Supabase CLI detected');
|
|
42
|
+
console.log('');
|
|
43
|
+
|
|
44
|
+
// Run deployment script
|
|
45
|
+
const deployScript = join(__dirname, '..', 'scripts', 'deploy-functions.sh');
|
|
46
|
+
|
|
47
|
+
if (!existsSync(deployScript)) {
|
|
48
|
+
console.error('❌ Deployment script not found:', deployScript);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log('📦 Deploying Edge Functions...');
|
|
53
|
+
console.log('');
|
|
54
|
+
|
|
55
|
+
const deploy = spawn('bash', [deployScript], {
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
cwd: join(__dirname, '..'),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
deploy.on('error', (error) => {
|
|
61
|
+
console.error('❌ Deployment failed:', error.message);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
deploy.on('close', (code) => {
|
|
66
|
+
if (code !== 0) {
|
|
67
|
+
console.error('\n❌ Deployment failed with code', code);
|
|
68
|
+
process.exit(code);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log('✅ Deployment complete!');
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log('🔐 Don\'t forget to configure secrets in Supabase Dashboard:');
|
|
75
|
+
console.log(' Settings → Edge Functions → Add secrets');
|
|
76
|
+
console.log('');
|
|
77
|
+
process.exit(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Email Automator Setup CLI
|
|
5
|
+
* Interactive setup for Email Automator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { dirname, join } from 'path';
|
|
10
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
11
|
+
import readline from 'readline';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
const rl = readline.createInterface({
|
|
17
|
+
input: process.stdin,
|
|
18
|
+
output: process.stdout,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function question(query) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
rl.question(query, resolve);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function setup() {
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log('🎯 Email Automator Setup');
|
|
30
|
+
console.log('========================');
|
|
31
|
+
console.log('');
|
|
32
|
+
|
|
33
|
+
const envPath = join(__dirname, '..', '.env');
|
|
34
|
+
const envExamplePath = join(__dirname, '..', '.env.example');
|
|
35
|
+
|
|
36
|
+
// Check if .env already exists
|
|
37
|
+
if (existsSync(envPath)) {
|
|
38
|
+
const overwrite = await question('⚠️ .env file already exists. Overwrite? (y/N): ');
|
|
39
|
+
if (overwrite.toLowerCase() !== 'y') {
|
|
40
|
+
console.log('Setup cancelled.');
|
|
41
|
+
rl.close();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log('📝 Let\'s configure your Email Automator...');
|
|
48
|
+
console.log('');
|
|
49
|
+
|
|
50
|
+
// Supabase Configuration
|
|
51
|
+
console.log('1️⃣ Supabase Configuration');
|
|
52
|
+
console.log(' (Get these from: https://supabase.com/dashboard/project/_/settings/api)');
|
|
53
|
+
console.log('');
|
|
54
|
+
const supabaseUrl = await question(' Supabase URL: ');
|
|
55
|
+
const supabaseAnonKey = await question(' Supabase Anon Key: ');
|
|
56
|
+
|
|
57
|
+
// API Configuration
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log('2️⃣ API Configuration');
|
|
60
|
+
const port = await question(' API Port [3004]: ') || '3004';
|
|
61
|
+
|
|
62
|
+
// LLM Configuration
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log('3️⃣ LLM Configuration');
|
|
65
|
+
const llmApiKey = await question(' OpenAI API Key: ');
|
|
66
|
+
const llmModel = await question(' Model [gpt-4o-mini]: ') || 'gpt-4o-mini';
|
|
67
|
+
|
|
68
|
+
// OAuth Configuration (optional)
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log('4️⃣ OAuth Configuration (Optional - skip for now if not ready)');
|
|
71
|
+
const gmailClientId = await question(' Gmail Client ID [skip]: ') || 'your_gmail_client_id';
|
|
72
|
+
const gmailClientSecret = await question(' Gmail Client Secret [skip]: ') || 'your_gmail_client_secret';
|
|
73
|
+
|
|
74
|
+
// Generate encryption key
|
|
75
|
+
const encryptionKey = Array.from({ length: 32 }, () =>
|
|
76
|
+
Math.random().toString(36).substring(2, 3)
|
|
77
|
+
).join('');
|
|
78
|
+
|
|
79
|
+
// Create .env content
|
|
80
|
+
const envContent = `# Supabase Configuration
|
|
81
|
+
VITE_SUPABASE_URL=${supabaseUrl}
|
|
82
|
+
VITE_SUPABASE_ANON_KEY=${supabaseAnonKey}
|
|
83
|
+
|
|
84
|
+
# API Configuration
|
|
85
|
+
VITE_API_URL=http://localhost:${port}
|
|
86
|
+
PORT=${port}
|
|
87
|
+
|
|
88
|
+
# OpenAI / LLM Configuration
|
|
89
|
+
LLM_API_KEY=${llmApiKey}
|
|
90
|
+
LLM_BASE_URL=https://api.openai.com/v1
|
|
91
|
+
LLM_MODEL=${llmModel}
|
|
92
|
+
|
|
93
|
+
# Security
|
|
94
|
+
JWT_SECRET="dev-secret-change-in-production"
|
|
95
|
+
TOKEN_ENCRYPTION_KEY="${encryptionKey}"
|
|
96
|
+
|
|
97
|
+
# Development
|
|
98
|
+
DISABLE_AUTH=true
|
|
99
|
+
|
|
100
|
+
# Gmail OAuth (optional)
|
|
101
|
+
GMAIL_CLIENT_ID=${gmailClientId}
|
|
102
|
+
GMAIL_CLIENT_SECRET=${gmailClientSecret}
|
|
103
|
+
|
|
104
|
+
# Microsoft Graph (optional)
|
|
105
|
+
MS_GRAPH_CLIENT_ID=your_ms_graph_client_id
|
|
106
|
+
MS_GRAPH_TENANT_ID=common
|
|
107
|
+
MS_GRAPH_CLIENT_SECRET=your_ms_graph_client_secret
|
|
108
|
+
|
|
109
|
+
# Processing
|
|
110
|
+
EMAIL_BATCH_SIZE=20
|
|
111
|
+
SYNC_INTERVAL_MS=300000
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
// Write .env file
|
|
115
|
+
writeFileSync(envPath, envContent);
|
|
116
|
+
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log('✅ Configuration saved to .env');
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log('📚 Next Steps:');
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(' 1. Deploy Edge Functions:');
|
|
123
|
+
console.log(' $ supabase login');
|
|
124
|
+
console.log(' $ ./scripts/deploy-functions.sh');
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(' 2. Configure Edge Function secrets in Supabase Dashboard:');
|
|
127
|
+
console.log(' Settings → Edge Functions → Add secrets');
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(' 3. Start Email Automator:');
|
|
130
|
+
console.log(' $ npx email-automator');
|
|
131
|
+
console.log(' or');
|
|
132
|
+
console.log(' $ npm run dev:api');
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log('📖 Documentation: https://github.com/therealtimex/email-automator');
|
|
135
|
+
console.log('');
|
|
136
|
+
|
|
137
|
+
rl.close();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
setup().catch((error) => {
|
|
141
|
+
console.error('❌ Setup failed:', error.message);
|
|
142
|
+
rl.close();
|
|
143
|
+
process.exit(1);
|
|
144
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Email Automator CLI
|
|
5
|
+
* Main command to run the Email Automator API server
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Parse command line arguments
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
|
|
18
|
+
// Default port
|
|
19
|
+
let port = '3004';
|
|
20
|
+
const portIndex = args.indexOf('--port');
|
|
21
|
+
if (portIndex !== -1 && args[portIndex + 1]) {
|
|
22
|
+
port = args[portIndex + 1];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const noUi = args.includes('--no-ui');
|
|
26
|
+
|
|
27
|
+
console.log('🚀 Starting Email Automator...');
|
|
28
|
+
console.log(`📡 Port: ${port}`);
|
|
29
|
+
if (noUi) console.log('🖥️ Mode: No-UI');
|
|
30
|
+
console.log('');
|
|
31
|
+
|
|
32
|
+
// Path to server
|
|
33
|
+
const serverPath = join(__dirname, '..', 'api', 'server.ts');
|
|
34
|
+
|
|
35
|
+
// Start server with tsx
|
|
36
|
+
const server = spawn('npx', ['tsx', serverPath, ...args], {
|
|
37
|
+
stdio: 'inherit',
|
|
38
|
+
env: { ...process.env, PORT: port },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
server.on('error', (error) => {
|
|
42
|
+
console.error('❌ Failed to start Email Automator:', error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
server.on('close', (code) => {
|
|
47
|
+
if (code !== 0) {
|
|
48
|
+
console.log(`\n⚠️ Email Automator stopped with code ${code}`);
|
|
49
|
+
}
|
|
50
|
+
process.exit(code || 0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Handle graceful shutdown
|
|
54
|
+
process.on('SIGINT', () => {
|
|
55
|
+
console.log('\n\n⏹️ Shutting down Email Automator...');
|
|
56
|
+
server.kill('SIGINT');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
process.on('SIGTERM', () => {
|
|
60
|
+
server.kill('SIGTERM');
|
|
61
|
+
});
|