Skip to main content

Integrations & Third-Party Services

External integrations and services used by Profile PS3.

AWS S3 File Storage

Profile PS3 uses AWS S3 for scalable, reliable project attachment storage.

Configuration

Package: league/flysystem-aws-s3-v3: 3.25.1

Environment Variables:

FILESYSTEM_DISK=s3
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=profileps3-attachments
AWS_URL=https://profileps3-attachments.s3.us-east-1.amazonaws.com
AWS_USE_PATH_STYLE_ENDPOINT=false

File Storage Structure

s3://profileps3-attachments/
├── attachments/
│ ├── projects/
│ │ ├── 2026/
│ │ │ ├── 01/
│ │ │ │ ├── project-123-spec.pdf
│ │ │ │ └── project-124-photo.jpg
│ │ │ └── 02/
│ │ └── 2025/
│ ├── reports/
│ └── exports/
└── temp/

Usage Examples

Upload File:

use Illuminate\Support\Facades\Storage;

$path = Storage::disk('s3')->putFileAs(
'attachments/projects/' . date('Y/m'),
$request->file('attachment'),
$filename
);

Generate Signed URL (time-limited download):

$url = Storage::disk('s3')->temporaryUrl(
$attachment->path,
now()->addMinutes(30)
);

Delete File:

Storage::disk('s3')->delete($attachment->path);

Project Attachments Model

Model: App\Models\Attachment

Eloquent Observer: AttachmentObserver

  • Automatically deletes S3 files when attachment record deleted
  • Handles cleanup on project deletion

File Validation:

// In Filament FileUpload component
Forms\Components\FileUpload::make('file')
->disk('s3')
->directory('attachments/projects')
->maxSize(50 * 1024) // 50MB
->acceptedFileTypes([
'application/pdf',
'image/jpeg',
'image/png',
'image/webp',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
])
->preserveFilenames()
->downloadable()
->openable();

Local Development Fallback

For development without AWS credentials:

FILESYSTEM_DISK=local

Files stored in storage/app/attachments/

S3 Bucket Configuration

Bucket Policy (read-only for authenticated users):

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT_ID:user/profileps3-app"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::profileps3-attachments/*"
}
]
}

CORS Configuration:

[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedOrigins": ["https://profileps3.com"],
"ExposeHeaders": ["ETag"]
}
]

Mailgun Email Service

Transactional and notification emails via Mailgun.

Configuration

Package: symfony/mailgun-mailer: ^7.3

Environment Variables:

MAIL_MAILER=mailgun
[email protected]
MAIL_FROM_NAME="Profile PS3"

MAILGUN_DOMAIN=mg.profileps3.com
MAILGUN_SECRET=key-xxxxxxxxxxxxxxxxxxxxxxxx
MAILGUN_ENDPOINT=api.mailgun.net

Email Types

Account Management:

  • User registration confirmation
  • Account approval notification
  • Account denial notification
  • Password reset
  • Email verification

Project Notifications:

  • New project created (to regional manager)
  • Project milestone achieved
  • Project archived

MFA:

  • MFA setup instructions
  • Recovery code generation

Marketing (opt-in):

  • Product updates
  • Feature announcements
  • Monthly newsletter

Sending Emails

Using Mailables:

use App\Mail\AccountApproved;
use Illuminate\Support\Facades\Mail;

Mail::to($user->email)->send(new AccountApproved($user));

Using Notifications:

use App\Notifications\ProjectCreatedNotification;

$regionalManager->notify(new ProjectCreatedNotification($project));

Queue for Background Sending:

Mail::to($user)->queue(new AccountApproved($user));

Email Preferences

Users can manage email subscriptions via profile:

Model: User has get_product_updates boolean field

Check Before Sending:

if ($user->get_product_updates) {
Mail::to($user)->send(new ProductUpdateEmail());
}

Mailgun Features

Tracking:

  • Open tracking
  • Click tracking
  • Delivery tracking

Analytics Dashboard:

  • Delivery rate
  • Bounce rate
  • Complaint rate
  • Open rate
  • Click rate

Suppression Lists:

  • Automatic bounce management
  • Unsubscribe handling
  • Complaint tracking

Email Templates

Located in resources/views/emails/

Example (welcome.blade.php):

<x-mail::message>
# Welcome to Profile PS3

Your account has been approved! You can now log in and start managing your erosion control projects.

<x-mail::button :url="route('filament.app.auth.login')">
Log In
</x-mail::button>

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>

Testing Emails

Development (log emails to file):

MAIL_MAILER=log

View logs: storage/logs/laravel.log

Testing (use Mailtrap or similar):

MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password

BDO Export Integration

Profile PS3 exports data to BDO (external system) for synchronization.

Export Configuration

Model: App\Models\BdoExport

Supported Entities:

  • Users
  • Projects
  • Slopes
  • Channels
  • ECBs (Erosion Control Blankets)
  • HECPs (Hydraulically Applied Products)
  • TRMs (Turf Reinforcement Mats)

Incremental Export System

Model: App\Models\BdoExportCheckpoint

Tracks last export timestamp and data checksum for each record.

Logic:

// Only export changed records
$recordsToExport = Project::where(function($query) {
$query->whereDoesntHave('bdoCheckpoint')
->orWhereHas('bdoCheckpoint', function($q) {
$q->where('updated_at', '>', DB::raw('last_exported_at'));
});
})->get();

Export Process

Artisan Command:

php artisan bdo:export {entity}

# Examples
php artisan bdo:export users
php artisan bdo:export projects
php artisan bdo:export ecbs

Scheduled Export (in app/Console/Kernel.php):

$schedule->command('bdo:export users')->daily()->at('02:00');
$schedule->command('bdo:export projects')->daily()->at('02:30');
$schedule->command('bdo:export ecbs')->weekly()->sundays()->at('03:00');

Export Format

JSON Export Structure:

{
"export_id": "export-2026-04-21-projects",
"entity_type": "projects",
"timestamp": "2026-04-21T02:30:00Z",
"record_count": 145,
"records": [
{
"id": 123,
"projectNum": "PRJ-20260415-001",
"userId": 45,
"projectAddress": "123 Main St",
"city": "Denver",
"state": "CO",
"projectType": "Slope Protection",
"active": "active",
"created_at": "2026-04-15T10:30:00Z",
"updated_at": "2026-04-20T14:22:00Z"
}
]
}

Export History

View export history in admin panel:

Resource: BdoExportResource

Fields Displayed:

  • Export date/time
  • Entity type
  • Record count
  • Status (pending/success/failed)
  • Error message (if failed)

Manual Export Trigger

Admins can manually trigger exports via admin panel:

Action: "Run Export" button in BdoExportResource

Error Handling

Retry Logic:

  • Failed exports automatically retry 3 times
  • Exponential backoff between retries
  • Errors logged to storage/logs/bdo-export.log

Notifications:

  • Admin notified of failed exports
  • Weekly summary of export status

Future Integration Opportunities

Planned Integrations:

  • Payment processing (Stripe/PayPal) for subscription billing
  • CRM integration (Salesforce, HubSpot) for lead management
  • GIS mapping services (ArcGIS, Mapbox) for project visualization
  • Weather API (OpenWeather) for site condition monitoring
  • Mobile app API for field data collection
  • Webhook notifications for real-time updates
  • SSO integration (Google, Microsoft Azure AD) for enterprise customers