diff --git a/README.md b/README.md index ea57359..f067fe0 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ AdonisJS v5 package for email validation and a DB-backed suppression list. ## Install ```bash -npm i adonis-email-guard -node ace configure adonis-email-guard +npm i @ezycourse/adonis-email-guard +node ace configure @ezycourse/adonis-email-guard ``` The configure step copies `config/email-guard.ts` into your app and registers the provider. @@ -55,8 +55,6 @@ if (!c.valid || c.suppressed) return; // do not send await EmailGuard.suppress('bounce@x.com', 'bounce', 'webhook', {provider: 'postmark'}); await EmailGuard.isSuppressed('bounce@x.com'); // true await EmailGuard.find('bounce@x.com'); -await EmailGuard.list({reason: 'bounce', limit: 50}); -await EmailGuard.count({source: 'webhook'}); await EmailGuard.unsuppress('bounce@x.com'); ``` @@ -84,8 +82,6 @@ Returns `{ valid, email, reason?, checks, suggestion?, mxHost? }`. | `unsuppress(email)` | Delete. Returns `true` if a row was removed. | | `isSuppressed(email)` | Boolean lookup. | | `find(email)` | Row or `null`. | -| `list(filters?)` | Filter by `reason`/`source`/`search`, with paging. | -| `count(filters?)` | Same filters, returns a count. | | `ensureTable()` | Idempotent `CREATE TABLE IF NOT EXISTS`. | ## Suppression table schema diff --git a/adonis-typings/email-guard.ts b/adonis-typings/email-guard.ts index ab14fc1..70ddc4e 100644 --- a/adonis-typings/email-guard.ts +++ b/adonis-typings/email-guard.ts @@ -47,14 +47,6 @@ declare module '@ioc:Adonis/Addons/EmailGuard' { updated_at: Date; }; - export type SuppressionListFilters = { - reason?: SuppressionReason; - source?: SuppressionSource; - search?: string; - limit?: number; - offset?: number; - }; - export type EmailGuardConfig = { tableName?: string; /** Run `CREATE TABLE IF NOT EXISTS` for the suppression table on app boot. Default true. */ @@ -82,8 +74,6 @@ declare module '@ioc:Adonis/Addons/EmailGuard' { ): Promise; unsuppress(email: string): Promise; find(email: string): Promise; - list(filters?: SuppressionListFilters): Promise; - count(filters?: Omit): Promise; /** Validates and, on failure, optionally records a suppression. Combines validate + isSuppressed. */ check( email: string, diff --git a/instructions.md b/instructions.md index 28e1dfa..96db55c 100644 --- a/instructions.md +++ b/instructions.md @@ -2,13 +2,13 @@ Setup steps after install: -1. **Run the configure command** (already done if you used `node ace configure adonis-email-guard`). This copies `config/email-guard.ts` into your app. +1. **Run the configure command** (already done if you used `node ace configure @ezycourse/adonis-email-guard`). This copies `config/email-guard.ts` into your app. 2. **Verify the provider was registered** in `.adonisrc.json`: ```json { - "providers": ["./providers/AppProvider", "adonis-email-guard"] + "providers": ["./providers/AppProvider", "@ezycourse/adonis-email-guard"] } ``` @@ -17,7 +17,7 @@ Setup steps after install: ```json { "compilerOptions": { - "types": ["@adonisjs/core", "@adonisjs/lucid", "adonis-email-guard"] + "types": ["@adonisjs/core", "@adonisjs/lucid", "@ezycourse/adonis-email-guard"] } } ``` diff --git a/package.json b/package.json index b337718..fa7852e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "adonis-email-guard", + "name": "@ezycourse/adonis-email-guard", "version": "0.1.0", "description": "AdonisJS v5 package: email validation (syntax, MX, SMTP, disposable, spam-trap) + DB-backed suppression list with configurable table and auto-create.", "main": "build/providers/EmailGuardProvider.js", @@ -62,9 +62,9 @@ }, "adonisjs": { "instructionsMd": "./build/instructions.md", - "types": "adonis-email-guard", + "types": "@ezycourse/adonis-email-guard", "providers": [ - "adonis-email-guard" + "@ezycourse/adonis-email-guard" ], "templates": { "config": [ @@ -76,7 +76,7 @@ } }, "publishConfig": { - "access": "public", + "access": "restricted", "tag": "latest" } } diff --git a/src/EmailGuard.ts b/src/EmailGuard.ts index cb78c2b..f8fb2c6 100644 --- a/src/EmailGuard.ts +++ b/src/EmailGuard.ts @@ -4,7 +4,6 @@ import type { EmailGuardConfig, EmailGuardContract, EmailValidationResult, - SuppressionListFilters, SuppressionReason, SuppressionRow, SuppressionSource, @@ -65,16 +64,6 @@ export class EmailGuard implements EmailGuardContract { return this.suppression.unsuppress(email); } - public list(filters?: SuppressionListFilters): Promise { - return this.suppression.list(filters); - } - - public count( - filters?: Omit - ): Promise { - return this.suppression.count(filters); - } - public ensureTable(): Promise { return this.suppression.ensureTable(); } diff --git a/src/Suppression/index.ts b/src/Suppression/index.ts index 7537d9b..4777d79 100644 --- a/src/Suppression/index.ts +++ b/src/Suppression/index.ts @@ -1,6 +1,5 @@ import type {DatabaseContract, QueryClientContract} from '@ioc:Adonis/Lucid/Database'; import type { - SuppressionListFilters, SuppressionReason, SuppressionRow, SuppressionSource, @@ -104,29 +103,6 @@ export class SuppressionService { return Number(count) > 0; } - public async list(filters: SuppressionListFilters = {}): Promise { - const q = this.client().from(this.tableName).select('*'); - if (filters.reason) q.where('reason', filters.reason); - if (filters.source) q.where('source', filters.source); - if (filters.search) q.where('email', 'like', `%${filters.search.toLowerCase()}%`); - if (filters.limit) q.limit(filters.limit); - if (filters.offset) q.offset(filters.offset); - q.orderBy('id', 'desc'); - const rows = await q; - return rows.map((r: any) => this.hydrate(r)); - } - - public async count( - filters: Omit = {} - ): Promise { - const q = this.client().from(this.tableName); - if (filters.reason) q.where('reason', filters.reason); - if (filters.source) q.where('source', filters.source); - if (filters.search) q.where('email', 'like', `%${filters.search.toLowerCase()}%`); - const row = (await q.count('* as total').first()) as {total: number | string} | undefined; - return row ? Number(row.total) : 0; - } - private hydrate(row: any): SuppressionRow { let metadata: Record | null = null; if (row.metadata !== null && row.metadata !== undefined) { diff --git a/test/suppression.spec.ts b/test/suppression.spec.ts index 36bdd31..2defa63 100644 --- a/test/suppression.spec.ts +++ b/test/suppression.spec.ts @@ -51,8 +51,8 @@ describe('SuppressionService', () => { assert.equal(updated.source, 'manual'); assert.deepEqual(updated.metadata, {note: 'updated'}); - const all = await service.list(); - assert.equal(all.length, 1); + const rows = await k(TABLE).select('id'); + assert.equal(rows.length, 1); }); it('handles null metadata', async () => { @@ -82,49 +82,4 @@ describe('SuppressionService', () => { assert.equal(await service.isSuppressed('gone@x.com'), false); }); }); - - describe('list / count', () => { - beforeEach(async () => { - await service.suppress('one@x.com', 'bounce', 'webhook'); - await service.suppress('two@x.com', 'bounce', 'manual'); - await service.suppress('three@x.com', 'complaint', 'webhook'); - await service.suppress('four@x.com', 'manual', 'manual'); - }); - - it('filters by reason', async () => { - const rows = await service.list({reason: 'bounce'}); - assert.equal(rows.length, 2); - assert.equal(await service.count({reason: 'bounce'}), 2); - }); - - it('filters by source', async () => { - const rows = await service.list({source: 'webhook'}); - assert.equal(rows.length, 2); - assert.equal(await service.count({source: 'webhook'}), 2); - }); - - it('filters by search (LIKE on email)', async () => { - const rows = await service.list({search: 'three'}); - assert.equal(rows.length, 1); - assert.equal(rows[0].email, 'three@x.com'); - }); - - it('paginates', async () => { - const page1 = await service.list({limit: 2, offset: 0}); - const page2 = await service.list({limit: 2, offset: 2}); - assert.equal(page1.length, 2); - assert.equal(page2.length, 2); - assert.notEqual(page1[0].email, page2[0].email); - }); - - it('orders newest first', async () => { - const all = await service.list(); - assert.equal(all[0].email, 'four@x.com'); - assert.equal(all[all.length - 1].email, 'one@x.com'); - }); - - it('counts all when no filter', async () => { - assert.equal(await service.count(), 4); - }); - }); });