StageFinder is a comprehensive job application management system that helps users find companies, generate personalized cover letters, and manage their job applications with automated email sending capabilities.
- 🔍 Company Search: Search for French companies using INSEE SIRENE API
- 📄 CV Management: Secure encrypted storage for CVs
- 📝 Template System: Create and manage cover letter templates with placeholders
- 🎯 PDF Generation: Generate professional PDF cover letters
- 📧 Email Automation: Send applications via user's own SMTP configuration
- ⭐ Favorites & Tags: Organize companies with favorites and tags
- 🔐 Security: Encrypted file storage, secure authentication
- 🗂️ Application Tracking: Track all sent applications with status
- 🔒 GDPR Compliant: Full data deletion support
- Backend: Symfony 6.3, PHP 8.2+
- Database: MySQL 8.0 / MariaDB
- Cache: Redis
- PDF Generation: Dompdf
- Email: Symfony Mailer with SMTP
- Queue: Symfony Messenger
- Encryption: Libsodium
- Containerization: Docker & Docker Compose
- Docker & Docker Compose
- Git
- (Optional) Composer if running locally without Docker
- (Optional) SIRENE API token from INSEE
git clone <repository-url>
cd stagefinderCreate .env.local file:
cp .env .env.localEdit .env.local with your configuration:
APP_ENV=dev
APP_SECRET=change_this_to_a_random_32_char_string
# Database
DATABASE_URL=mysql://symfony:symfony@db:3306/stagefinder
# Redis
REDIS_URL=redis://redis:6379
# Encryption key (32 bytes base64 encoded)
# Generate with: echo -n "your-32-byte-secret-key-here!" | base64
ENCRYPTION_KEY=base64:YW5leGFtcGxlMzJieXRlc2VjcmV0a2V5MTIzNDU2Nzg=
# File storage
CLOUD_STORAGE_PATH=/var/www/uploads
# SIRENE API (optional - for company sync)
SIRENE_TOKEN=your_insee_api_token_here
# Messenger
MESSENGER_TRANSPORT_DSN=doctrine://defaultdocker-compose up -dThis will start:
- MySQL database (port 3306)
- Redis (port 6379)
- PHP-FPM application
- Nginx web server (port 8080)
- Worker for async tasks
# Enter the app container
docker-compose exec app bash
# Install dependencies
composer install
# Create database schema
php bin/console doctrine:migrations:migrate --no-interaction
# Create admin user (optional)
php bin/console app:create-admin admin@example.com password123 "Admin Name"
# Seed sample companies for Chartres (optional)
php bin/console app:seed-companies- Web interface: http://localhost:8080
- API: http://localhost:8080/api
- PHP 8.2+ with extensions: pdo_mysql, zip, intl, gd, mbstring, sodium
- MySQL 8.0+
- Composer
- Redis (optional but recommended)
# Install dependencies
composer install
# Configure database in .env.local
DATABASE_URL=mysql://user:password@localhost:3306/stagefinder
# Create database
php bin/console doctrine:database:create
# Run migrations
php bin/console doctrine:migrations:migrate
# Start Symfony server
symfony server:start
# In another terminal, start worker
php bin/console messenger:consume async -vv# Create account via API
curl -X POST http://localhost:8080/api/register \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"secure123","fullName":"John Doe"}'
# Login
curl -X POST http://localhost:8080/api/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"secure123"}'# Search in Chartres with 25km radius
curl "http://localhost:8080/api/companies?zones=chartres,28&rad=25&page=1"curl -X POST http://localhost:8080/api/cv/upload \
-F "file=@/path/to/your/cv.pdf"curl -X POST http://localhost:8080/api/templates \
-H "Content-Type: application/json" \
-d '{
"name": "My Cover Letter",
"templateText": "Madame, Monsieur,\n\nJe vous écris pour postuler chez {{COMPANY_NAME}}..."
}'curl -X POST http://localhost:8080/api/generate-cover \
-H "Content-Type: application/json" \
-d '{
"company_id": 1,
"template_id": 1,
"cv_id": 1
}'curl -X POST http://localhost:8080/api/smtp/connect \
-H "Content-Type: application/json" \
-d '{
"host": "smtp.gmail.com",
"port": 587,
"username": "your-email@gmail.com",
"password": "your-app-password",
"tls": true
}'curl -X POST http://localhost:8080/api/send \
-H "Content-Type: application/json" \
-d '{
"company_id": 1,
"cover_pdf_id": 1,
"cv_id": 1,
"smtp_config_id": 1,
"subject": "Candidature spontanée",
"body_override": "Veuillez trouver ci-joint..."
}'Use these in your templates:
{{COMPANY_NAME}}- Company name{{COMPANY_ADDRESS}}- Full company address{{COMPANY_CITY}}- Company city{{COMPANY_POSTAL_CODE}}- Postal code{{USER_FULLNAME}}- Your full name{{USER_EMAIL}}- Your email{{CURRENT_DATE}}- Current date{{POSITION}}- Position (if specified)
You can also use defaults:
{{COMPANY_EMAIL | default('recrutement@company.com')}}
php bin/console app:sync-companies --city=Chartres --radius=25php bin/console app:create-admin admin@example.com password123 "Admin User"php bin/console app:seed-companiesphp bin/console messenger:consume async -vvphp bin/console app:prune-old-files --days=90./vendor/bin/phpunit./vendor/bin/phpunit tests/Service/TemplateRendererTest.php./vendor/bin/phpunit --coverage-html coverageComplete API documentation is available in /docs/API.md
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/register | Create new account |
| POST | /api/login | Login |
| GET | /api/me | Get current user |
| GET | /api/companies | Search companies |
| GET | /api/companies/{id} | Get company details |
| POST | /api/favorites | Add favorite |
| GET | /api/favorites | List favorites |
| POST | /api/templates | Create template |
| GET | /api/templates | List templates |
| POST | /api/cv/upload | Upload CV |
| POST | /api/generate-cover | Generate cover letter |
| POST | /api/smtp/connect | Configure SMTP |
| POST | /api/send | Send application |
| GET | /api/applications | List applications |
| POST | /api/account/delete | Delete account (GDPR) |
- ✅ Password hashing with Argon2id
- ✅ File encryption with Libsodium
- ✅ CSRF protection
- ✅ Rate limiting on sensitive endpoints
- ✅ Input validation and sanitization
- ✅ SQL injection prevention (Doctrine ORM)
- ✅ XSS prevention in templates
- ✅ Secure SMTP credential storage
- Users can delete their account and all associated data
- Encrypted file storage
- Data minimization
- Consent management
- Right to access (export data)
- Right to erasure
# Check if MySQL is running
docker-compose ps
# Check logs
docker-compose logs db# Check worker logs
docker-compose logs worker
# Restart worker
docker-compose restart worker# Fix permissions
docker-compose exec app chown -R www-data:www-data /var/www/uploads
docker-compose exec app chmod -R 755 /var/www/htmlphp bin/console cache:clearAPP_ENV=prod
APP_DEBUG=0
APP_SECRET=<strong-random-secret>
DATABASE_URL=mysql://user:pass@prod-db:3306/stagefinder# Install production dependencies
composer install --no-dev --optimize-autoloader
# Build assets
php bin/console assets:install --env=prod
# Clear and warm up cache
php bin/console cache:clear --env=prod
php bin/console cache:warmup --env=prod- Change APP_SECRET to random value
- Change ENCRYPTION_KEY
- Use strong database passwords
- Enable HTTPS
- Set secure headers in Nginx
- Configure firewall rules
- Set up monitoring and logging
- Regular backups
- Keep dependencies updated
curl http://localhost:8080/api/health# Application logs
tail -f var/log/prod.log
# Docker logs
docker-compose logs -f app
docker-compose logs -f worker- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open Pull Request
Proprietary - All rights reserved
For issues and questions:
- Create an issue on GitHub
- Contact: support@stagefinder.example
- LinkedIn integration
- Multi-language support
- Advanced analytics
- Mobile app
- AI-powered cover letter suggestions
- Integration with more company databases
- Calendar integration for interview scheduling
Made with ❤️ for job seekers