Skip to content

Commit dd3d6ea

Browse files
committed
v0.5.0 add email notifications blacklist emails and export field control
1 parent d25c82c commit dd3d6ea

22 files changed

Lines changed: 1016 additions & 253 deletions

CHANGELOG.md

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,32 @@
11
# Changelog
22

3+
## v0.5.0
4+
5+
- added blocked email and blocked domain management
6+
- added email notification center with `log_only` and `mail` modes
7+
- added dashboard 7-day trend, top forms and country summary widgets
8+
- added export field selection for inquiry CSV export
9+
- enhanced API receive flow with blocked email/domain checks
10+
- added `blacklist_emails` table and `email_notifications` system setting
11+
- logged notification delivery attempts into system logs
12+
313
## v0.4.0
414

5-
- Added site-level Field Mapping JSON
6-
- Added admin note editing in inquiry detail page
7-
- Added inquiry list filter for inquiries with or without notes
8-
- Added Spam Rule Center page in backend
9-
- Added configurable rules for honeypot, links, duplicates, rate limits, keywords, and disposable email domains
10-
- Added `system_settings` model and spam rule service
11-
- Added database upgrade script for v0.4.0
12-
- Updated receive API to apply field mapping before validation
13-
- Updated receive API to include mapped payload in `extra_data` when mappings are used
15+
- added site field mapping JSON support
16+
- added admin note editing in inquiry detail page
17+
- added spam rule center page and settings persistence
1418

1519
## v0.3.0
1620

17-
- Added site create and edit functions
18-
- Added API token rotation
19-
- Added signature secret rotation
20-
- Added optional HMAC signature verification per site
21-
- Added system logs page
22-
- Added CSV export from inquiry list
23-
- Added blocked IP delete action
24-
- Updated dashboard with v0.3.0 summary and recent logs
25-
- Added database upgrade script for v0.3.0
26-
- Added signed PHP forwarder example
21+
- added site create and edit management
22+
- added HMAC signature verification support
23+
- added CSV export and system logs page
2724

2825
## v0.2.0
2926

30-
- Added unified receive API endpoint
31-
- Added API health check endpoint
32-
- Added `site_key + api_token` validation
33-
- Added required field validation for `name`, `email`, `content`
34-
- Added blocked IP check before insert
35-
- Added basic spam checks for honeypot, duplicate content, link count, IP/email rate limit
36-
- Added storage for `extra_data` and `raw_payload`
37-
- Added inquiry filters in the backend
38-
- Added quick status update actions
39-
- Added blocked IP add form in the backend
40-
- Added API quick start info in dashboard and sites page
41-
- Added GitHub Actions build-release workflow
42-
- Added PHP and JavaScript integration examples
27+
- added unified inquiry receive API
28+
- added base spam detection and payload storage
4329

4430
## v0.1.0
4531

46-
- Initial scaffold version
47-
- Added login/logout
48-
- Added dashboard, inquiries, detail, sites, blocked IPs, profile pages
49-
- Added schema and seed files
50-
- Added basic project structure and UI
32+
- initial scaffold with login, dashboard and basic inquiry pages

README.md

Lines changed: 54 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,87 @@
1-
# Inquiry Management System
1+
# Inquiry Management System v0.5.0
22

3-
Version: **v0.4.0**
3+
A lightweight **pure PHP + MySQL** inquiry hub for collecting form submissions from multiple websites and managing them in one backend.
44

5-
A pure PHP + MySQL inquiry management system for collecting inquiry forms from multiple websites into one centralized backend.
5+
## What is included in v0.5.0
66

7-
## v0.4.0 Highlights
7+
- Multi-site inquiry receive API
8+
- Site management with token and signature secret rotation
9+
- Field mapping JSON per site
10+
- Inquiry list, detail page, note management and status flow
11+
- CSV export with selectable export fields
12+
- Spam rule center
13+
- Blocked IP management
14+
- Blocked email / domain management
15+
- Email notification center
16+
- Dashboard with 7-day trend, top forms and country summary
17+
- System logs
18+
- GitHub Actions ZIP build workflow
819

9-
- Added per-site **Field Mapping JSON** so different form field names can map into the system's standard fields
10-
- Added **Admin Note** editing on the inquiry detail page
11-
- Added **Spam Rule Center** in the backend for honeypot, links, duplicates, rate limits, keywords, and disposable email domains
12-
- Added filtering by **Has Note** in inquiry list
13-
- API now stores mapped payload data into `extra_data` when field mapping is applied
14-
15-
## Environment
16-
17-
- PHP 8.1+
18-
- MySQL 5.7+ or MySQL 8+
19-
- Apache or Nginx
20-
21-
## Installation
22-
23-
1. Create a database, for example: `inquiry_system`
24-
2. Import:
25-
- `database/schema.sql`
26-
- `database/seed.sql`
27-
3. Update database settings in `config/database.php`
28-
4. Point your web root to `public/`
29-
5. Open the project in your browser
30-
31-
## Upgrading from v0.3.0
32-
33-
If you already have a v0.3.0 database, run:
34-
35-
- `database/upgrade-v0.4.0.sql`
36-
37-
This adds:
38-
39-
- `field_mapping_json` on `inquiry_sites`
40-
- default `spam_rules` in `system_settings`
41-
42-
## Default Admin Account
20+
## Default admin account
4321

4422
- Username: `admin`
4523
- Password: `Admin@123456`
4624

47-
## Main Backend Routes
48-
49-
- `/login`
50-
- `/dashboard`
51-
- `/inquiries`
52-
- `/inquiries/export`
53-
- `/sites`
54-
- `/sites/edit?id=1`
55-
- `/logs`
56-
- `/tools/blacklist-ips`
57-
- `/tools/spam-rules`
58-
- `/profile`
25+
## New in this version
5926

60-
## API Routes
27+
### 1. Email notifications
6128

62-
### Health Check
29+
You can now configure notification delivery from:
6330

64-
`GET /api/v1/health`
31+
- `Tools > Email Notifications`
6532

66-
### Submit Inquiry
33+
Supported modes:
6734

68-
`POST /api/v1/inquiries/submit`
35+
- `log_only`: safe testing mode, writes notification attempts to system logs
36+
- `mail`: uses native PHP `mail()`
6937

70-
Supported payload types:
38+
### 2. Email and domain blacklist
7139

72-
- `application/json`
73-
- standard form POST
40+
You can now block:
7441

75-
### Minimum payload
42+
- a specific sender email
43+
- an entire email domain
7644

77-
```json
78-
{
79-
"site_key": "a_main",
80-
"api_token": "token_a_main_2026",
81-
"name": "John Smith",
82-
"email": "john@example.com",
83-
"content": "I want more information about your products."
84-
}
85-
```
45+
Manage them from:
8646

87-
### Optional fields
47+
- `Tools > Blocked Emails`
8848

89-
- `form_key`
90-
- `title`
91-
- `country`
92-
- `phone`
93-
- `address`
94-
- `from_company`
95-
- `source_url`
96-
- `referer_url`
97-
- `language`
98-
- `browser`
99-
- `device_type`
100-
- `submitted_at`
101-
- `client_ip`
102-
- `extra_data` (array)
49+
### 3. Better export control
10350

104-
Unknown fields will also be merged into `extra_data` automatically.
51+
The inquiry list page now lets you choose which CSV columns should be exported.
10552

106-
## Field Mapping JSON
53+
### 4. Dashboard enhancements
10754

108-
You can configure a site-level JSON mapping in the backend to transform external field names into the system's standard fields before validation and storage.
55+
The dashboard now shows:
10956

110-
Example:
57+
- 7-day inquiry trend
58+
- top forms
59+
- top countries
60+
- current notification configuration summary
11161

112-
```json
113-
{
114-
"name": ["fullname", "your_name"],
115-
"email": ["user_email", "contact_email"],
116-
"title": ["subject"],
117-
"content": ["message", "comments"],
118-
"from_company": ["company", "company_name"],
119-
"phone": ["mobile", "tel"]
120-
}
121-
```
122-
123-
## Signed Request Mode
124-
125-
For sites with **Require HMAC signature** enabled:
126-
127-
- Header: `X-Timestamp` = unix timestamp in seconds
128-
- Header: `X-Signature` = `hash_hmac('sha256', X-Timestamp + "\n" + raw_body, signature_secret)`
129-
130-
Recommended usage:
131-
132-
1. Your website backend builds the final request body
133-
2. Your website backend signs the raw body with the site's signature secret
134-
3. Your website backend sends the request to the central inquiry system
135-
136-
## Spam Rule Center
137-
138-
The backend now includes a configurable spam rule center for:
139-
140-
- honeypot field check
141-
- link threshold check
142-
- duplicate submission check
143-
- IP rate limit
144-
- email rate limit
145-
- keyword-based spam detection
146-
- disposable email domain detection
147-
148-
## Example Files
149-
150-
- `examples/php-forwarder.php`
151-
- `examples/php-signed-forwarder.php`
152-
- `examples/javascript-fetch-example.js`
62+
## Installation
15363

154-
## GitHub Actions
64+
### Fresh install
15565

156-
Workflow file:
66+
1. Create a MySQL database
67+
2. Import:
68+
- `database/schema.sql`
69+
- `database/seed.sql`
70+
3. Update `config/database.php`
71+
4. Point your web root to `public/`
15772

158-
- `.github/workflows/build-release.yml`
73+
### Upgrade from v0.4.0
15974

160-
It creates a ZIP package automatically when you push a tag like:
75+
Run:
16176

162-
```bash
163-
git tag v0.4.0
164-
git push origin v0.4.0
165-
```
77+
- `database/upgrade-v0.5.0.sql`
16678

167-
## Notes
79+
## API endpoint
16880

169-
Recommended production flow:
81+
- `POST /api/v1/inquiries/submit`
82+
- `GET /api/v1/health`
17083

171-
1. Website form submits to the current website backend
172-
2. The current website backend maps and forwards the payload to this central system
173-
3. This system validates, filters, stores, and manages the inquiry
84+
## Notes about outbound mail
17485

175-
This is safer than exposing tokens directly in front-end JavaScript.
86+
When using `transport = mail`, the hosting environment must already support outbound email for PHP `mail()`.
87+
If your server does not support it yet, use `log_only` first to verify the notification workflow safely.

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.4.0
1+
v0.5.0

app/Controllers/DashboardController.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use App\Models\Inquiry;
1010
use App\Models\InquiryLog;
1111
use App\Models\Site;
12+
use App\Services\EmailNotificationService;
1213

1314
final class DashboardController extends Controller
1415
{
@@ -26,6 +27,10 @@ public function index(): void
2627
'sites' => $siteModel->allWithStats(),
2728
'recentLogs' => $logModel->paginate(1, 6)['data'],
2829
'apiEndpoint' => base_url('api/v1/inquiries/submit'),
30+
'trendRows' => $inquiryModel->dailyTrend(7),
31+
'topForms' => $inquiryModel->topForms(6),
32+
'countrySummary' => $inquiryModel->countrySummary(6),
33+
'notificationSettings' => (new EmailNotificationService())->getSettings(),
2934
]);
3035
}
3136
}

app/Controllers/InquiryController.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ public function index(): void
2424
$inquiryModel = new Inquiry();
2525
$siteModel = new Site();
2626
$pagination = $inquiryModel->paginate($filters, $page, $perPage);
27+
$allowedExportFields = $inquiryModel->allowedExportColumns();
28+
$selectedExportFields = $this->collectExportFields(array_keys($allowedExportFields));
2729

2830
$this->view('dashboard/inquiries', [
2931
'pageTitle' => 'Inquiry Management',
3032
'pagination' => $pagination,
3133
'filters' => $filters,
3234
'sites' => $siteModel->all(),
3335
'csrfToken' => Csrf::token(),
36+
'allowedExportFields' => $allowedExportFields,
37+
'selectedExportFields' => $selectedExportFields,
3438
]);
3539
}
3640

@@ -134,18 +138,16 @@ public function updateNote(): void
134138
public function exportCsv(): void
135139
{
136140
$filters = $this->collectFilters();
137-
$rows = (new Inquiry())->exportRows($filters, 5000);
141+
$inquiryModel = new Inquiry();
142+
$allowedColumns = array_keys($inquiryModel->allowedExportColumns());
143+
$selectedFields = $this->collectExportFields($allowedColumns);
144+
$rows = $inquiryModel->exportRows($filters, $selectedFields, 5000);
138145

139-
(new InquiryLog())->create(null, Auth::id(), 'inquiries_exported', 'Exported ' . count($rows) . ' rows as CSV');
146+
(new InquiryLog())->create(null, Auth::id(), 'inquiries_exported', 'Exported ' . count($rows) . ' rows as CSV with fields: ' . implode(', ', $selectedFields));
140147

141148
send_csv_download(
142149
'inquiries-' . date('Ymd-His') . '.csv',
143-
[
144-
'id', 'site_name', 'form_key', 'status', 'name', 'email', 'title', 'content',
145-
'country', 'phone', 'address', 'from_company', 'source_url', 'referer_url',
146-
'ip', 'browser', 'device_type', 'language', 'admin_note', 'submitted_at',
147-
'created_at', 'updated_at', 'extra_data',
148-
],
150+
$selectedFields,
149151
$rows
150152
);
151153
}
@@ -167,4 +169,13 @@ private function collectFilters(): array
167169

168170
return $filters;
169171
}
172+
173+
private function collectExportFields(array $allowed): array
174+
{
175+
$fields = $_GET['fields'] ?? [];
176+
$fields = is_array($fields) ? array_map('strval', $fields) : [];
177+
$fields = array_values(array_intersect($fields, $allowed));
178+
179+
return $fields !== [] ? $fields : $allowed;
180+
}
170181
}

0 commit comments

Comments
 (0)