v2.0: Laravel + Inertia + Vue foundation complete
- Replaced Next.js + Express + React stack with Laravel + Inertia + Vue - Created database migrations for docket_entries, documents, subscriptions, admin_users - Built Eloquent models with relationships (DocketEntry, Document, Subscription, AdminUser) - Implemented HomeController with Inertia.js integration - Created Vue home page component matching v1.0 design exactly - Installed Laravel Breeze for authentication scaffolding - Configured Vite 7 for Vue 3 + TypeScript compilation - Updated .gitignore for Laravel project structure - Tested locally - website rendering correctly with empty database - All v1.0 Next.js/Express files removed, replaced with Laravel structure Technology Stack: - Backend: Laravel 12.43.1, PHP 8.3.28, Eloquent ORM - Frontend: Vue 3, TypeScript, Inertia.js, Tailwind CSS 3.x - Build: Vite 7.x, Composer 2.9.2 - Database: SQLite (dev), PostgreSQL (production) Next steps: Email subscription API, document downloads, admin dashboard
This commit is contained in:
parent
4e7c4252f8
commit
80657cc209
4881 changed files with 6317 additions and 408368 deletions
|
|
@ -1,15 +1,18 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{package.json,*.yml}]
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[compose.yaml]
|
||||
indent_size = 4
|
||||
69
.env
69
.env
|
|
@ -1,10 +1,65 @@
|
|||
# Production Environment Configuration
|
||||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:FabRZZvfMqwKRSPj2KYehBzYNrYI+hX247oLw58LRVs=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
# Database Configuration
|
||||
DB_PASSWORD=h3LvCItcZKcyRdsmeH1X+nVams7RAf89
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=ut02ymAdoHdrWAPgPsX25PMuhgqO54/Tow5JvgRdWeM=
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
# Email Configuration (Keep existing working values)
|
||||
GOOGLE_APP_PASSWORD=ojvlysraxwjriwzy
|
||||
# PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
|
|
|
|||
65
.env.example
Normal file
65
.env.example
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
# APP_MAINTENANCE_STORE=database
|
||||
|
||||
# PHP_CLI_SERVER_WORKERS=4
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
# CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_SCHEME=null
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
16
.env.prod
16
.env.prod
|
|
@ -1,16 +0,0 @@
|
|||
# Production Environment Configuration
|
||||
# Copy this file and set your actual production values
|
||||
|
||||
# Database Configuration
|
||||
DB_PASSWORD=your_secure_database_password_here
|
||||
|
||||
# JWT Configuration (Generate a secure random string)
|
||||
JWT_SECRET=your_secure_jwt_secret_here_minimum_32_characters
|
||||
|
||||
# Email Configuration (Keep existing working values)
|
||||
GOOGLE_APP_PASSWORD=ojvlysraxwjriwzy
|
||||
|
||||
# Optional: Override other settings if needed
|
||||
# SMTP_HOST=smtp.gmail.com
|
||||
# SMTP_PORT=587
|
||||
# GOOGLE_EMAIL=system@deafgain.org
|
||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
91
.gitignore
vendored
91
.gitignore
vendored
|
|
@ -1,47 +1,70 @@
|
|||
# macOS
|
||||
# Laravel & PHP
|
||||
*.log
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpactor.json
|
||||
.phpunit.result.cache
|
||||
/.fleet
|
||||
/.idea
|
||||
/.nova
|
||||
/.phpunit.cache
|
||||
/.vscode
|
||||
/.zed
|
||||
/auth.json
|
||||
/vendor
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
Thumbs.db
|
||||
|
||||
# Node modules
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
# Node & NPM
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
|
||||
# Environment variables
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
# Storage & Uploads
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/storage/pail
|
||||
/storage/app/public/*
|
||||
!/storage/app/public/.gitkeep
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
# Database
|
||||
/database/*.sqlite
|
||||
/database/*.sqlite-journal
|
||||
*.db
|
||||
*.db-shm
|
||||
*.db-wal
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
.next/
|
||||
# Build artifacts
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/bootstrap/cache/*
|
||||
!/bootstrap/cache/.gitkeep
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS generated files
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Composer
|
||||
/vendor/
|
||||
composer.lock
|
||||
|
||||
# NPM/Yarn
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
# Caddyfile for Eliza Kragh v. MAD Court Docket Website
|
||||
# This file should be placed in the Caddy configuration directory on the remote server
|
||||
|
||||
# Replace with your actual domain
|
||||
docket.example.com {
|
||||
# Enable automatic HTTPS
|
||||
tls {
|
||||
# Use Let's Encrypt for SSL certificates
|
||||
# email admin@example.com
|
||||
}
|
||||
|
||||
# Security headers
|
||||
header {
|
||||
# Security headers for legal document website
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
|
||||
# Remove server information
|
||||
-Server
|
||||
-X-Powered-By
|
||||
}
|
||||
|
||||
# Rate limiting for API endpoints
|
||||
rate_limit {
|
||||
zone api {
|
||||
key {remote_host}
|
||||
events 100
|
||||
window 1m
|
||||
}
|
||||
zone auth {
|
||||
key {remote_host}
|
||||
events 10
|
||||
window 1m
|
||||
}
|
||||
}
|
||||
|
||||
# API routes with rate limiting
|
||||
@api path /api/*
|
||||
rate_limit @api api
|
||||
|
||||
@auth path /api/auth/*
|
||||
rate_limit @auth auth
|
||||
|
||||
# Reverse proxy to frontend container
|
||||
reverse_proxy frontend:3000 {
|
||||
# Health check
|
||||
health_uri /
|
||||
health_interval 30s
|
||||
health_timeout 10s
|
||||
|
||||
# Load balancing (if multiple instances)
|
||||
lb_policy round_robin
|
||||
|
||||
# Headers for proper forwarding
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Host {host}
|
||||
}
|
||||
|
||||
# Logging for legal compliance
|
||||
log {
|
||||
output file /var/log/caddy/docket-access.log {
|
||||
roll_size 100mb
|
||||
roll_keep 10
|
||||
roll_keep_for 720h
|
||||
}
|
||||
format json
|
||||
level INFO
|
||||
}
|
||||
|
||||
# Error handling
|
||||
handle_errors {
|
||||
@5xx expression {http.error.status_code} >= 500
|
||||
respond @5xx "Service temporarily unavailable" 503
|
||||
|
||||
@4xx expression {http.error.status_code} >= 400 && {http.error.status_code} < 500
|
||||
respond @4xx "Page not found" 404
|
||||
}
|
||||
}
|
||||
|
||||
# Admin subdomain (optional - for enhanced security)
|
||||
admin.docket.example.com {
|
||||
tls {
|
||||
# email admin@example.com
|
||||
}
|
||||
|
||||
# Additional security for admin interface
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "DENY"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'"
|
||||
}
|
||||
|
||||
# Stricter rate limiting for admin
|
||||
rate_limit {
|
||||
zone admin {
|
||||
key {remote_host}
|
||||
events 20
|
||||
window 1m
|
||||
}
|
||||
}
|
||||
rate_limit admin
|
||||
|
||||
# Only allow admin routes
|
||||
@admin path /admin*
|
||||
handle @admin {
|
||||
reverse_proxy frontend:3000 {
|
||||
header_up Host {host}
|
||||
header_up X-Real-IP {remote_host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
# Redirect non-admin requests to main site
|
||||
redir https://docket.example.com{uri} permanent
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/admin-access.log {
|
||||
roll_size 50mb
|
||||
roll_keep 20
|
||||
roll_keep_for 2160h # 90 days
|
||||
}
|
||||
format json
|
||||
level INFO
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP to HTTPS redirect
|
||||
http://docket.example.com {
|
||||
redir https://docket.example.com{uri} permanent
|
||||
}
|
||||
|
||||
http://admin.docket.example.com {
|
||||
redir https://admin.docket.example.com{uri} permanent
|
||||
}
|
||||
|
|
@ -1,510 +0,0 @@
|
|||
# Eliza Kragh v. MAD Court Docket Website - Design Specification
|
||||
|
||||
## Project Overview
|
||||
A public-facing website to display court documents and filings for the ongoing lawsuit: **Eliza Kragh v. Montana Association of the Deaf**. The system provides public transparency for court proceedings while allowing administrative management of documents and user notifications.
|
||||
|
||||
## Technical Architecture
|
||||
|
||||
### System Components
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Next.js 15.4 │ │ Express.js 6 │ │ PostgreSQL 17.5 │
|
||||
│ Frontend │◄──►│ Backend API │◄──►│ Database │
|
||||
│ (Port 3000) │ │ (Port 3001) │ │ (Port 5432) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
│ ┌─────────────────┐ │
|
||||
│ │ Redis │ │
|
||||
└──────────────►│ Cache/Session │◄─────────────┘
|
||||
│ (Port 6379) │
|
||||
└─────────────────┘
|
||||
│
|
||||
┌─────────────────┐
|
||||
│ File System │
|
||||
│ /uploads/ │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
**Technology Stack (June 2025 Latest Versions):**
|
||||
- **Frontend**: Next.js 15.4.0 with React 19, TypeScript 6.0, Tailwind CSS
|
||||
- **Backend**: Node.js 22 LTS, Express.js 6.0, TypeScript 6.0, Prisma 5.0
|
||||
- **Database**: PostgreSQL 17.5 with Redis caching
|
||||
- **Infrastructure**: Docker Engine 28.1.1, Nginx reverse proxy
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Tables Structure
|
||||
```sql
|
||||
-- Docket Entries (main court filings)
|
||||
CREATE TABLE docket_entries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
date DATE NOT NULL,
|
||||
document_type VARCHAR(100) NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
court_notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Documents (PDFs associated with entries)
|
||||
CREATE TABLE documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
docket_entry_id INTEGER REFERENCES docket_entries(id) ON DELETE CASCADE,
|
||||
original_filename VARCHAR(255) NOT NULL,
|
||||
stored_filename VARCHAR(255) NOT NULL UNIQUE,
|
||||
file_path VARCHAR(500) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
summary TEXT,
|
||||
notes TEXT,
|
||||
is_primary BOOLEAN DEFAULT FALSE,
|
||||
file_size INTEGER NOT NULL,
|
||||
display_order INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Email Subscriptions
|
||||
CREATE TABLE subscriptions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
unsubscribe_token VARCHAR(255) UNIQUE
|
||||
);
|
||||
|
||||
-- Admin Users
|
||||
CREATE TABLE admin_users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
last_login TIMESTAMP
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX idx_docket_entries_date ON docket_entries(date DESC);
|
||||
CREATE INDEX idx_documents_docket_entry ON documents(docket_entry_id);
|
||||
CREATE INDEX idx_documents_display_order ON documents(docket_entry_id, display_order);
|
||||
CREATE INDEX idx_subscriptions_active ON subscriptions(is_active);
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Public Endpoints
|
||||
```
|
||||
GET /api/docket-entries
|
||||
Response: Array of docket entries with associated documents
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"id": 1,
|
||||
"date": "2024-12-15",
|
||||
"document_type": "Motion to Dismiss",
|
||||
"summary": "Defendant's motion...",
|
||||
"court_notes": "Motion filed within deadline...",
|
||||
"documents": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Main Document",
|
||||
"summary": "Primary motion document",
|
||||
"notes": "Filed by defendant's counsel",
|
||||
"is_primary": true,
|
||||
"display_order": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
GET /api/documents/:id/download
|
||||
Response: PDF file stream
|
||||
Headers: Content-Type: application/pdf
|
||||
|
||||
POST /api/subscribe
|
||||
Body: { "email": "user@example.com" }
|
||||
Response: { "success": true, "message": "Subscribed successfully" }
|
||||
|
||||
GET /api/unsubscribe/:token
|
||||
Response: Unsubscribe confirmation page
|
||||
```
|
||||
|
||||
### Admin Endpoints (Protected)
|
||||
```
|
||||
POST /api/auth/login
|
||||
Body: { "username": "admin", "password": "password" }
|
||||
Response: { "token": "jwt-token", "expires": "timestamp" }
|
||||
|
||||
POST /api/docket-entries
|
||||
Body: {
|
||||
"date": "2024-12-15",
|
||||
"document_type": "Motion",
|
||||
"summary": "Entry summary",
|
||||
"court_notes": "Court notes",
|
||||
"documents": [
|
||||
{
|
||||
"title": "Main Document",
|
||||
"summary": "Document summary",
|
||||
"notes": "Document notes",
|
||||
"is_primary": true,
|
||||
"file": "base64-encoded-pdf"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
PUT /api/docket-entries/:id
|
||||
Body: Updated entry data
|
||||
Response: Updated entry object
|
||||
|
||||
DELETE /api/docket-entries/:id
|
||||
Response: { "success": true }
|
||||
|
||||
POST /api/documents
|
||||
Body: FormData with file and metadata
|
||||
Response: Created document object
|
||||
|
||||
PUT /api/documents/:id
|
||||
Body: Updated document metadata
|
||||
Response: Updated document object
|
||||
|
||||
PUT /api/documents/:id/replace
|
||||
Body: FormData with new file
|
||||
Response: Updated document object
|
||||
|
||||
DELETE /api/documents/:id
|
||||
Response: { "success": true }
|
||||
```
|
||||
|
||||
## File Management System
|
||||
|
||||
### UUID-Based Naming Scheme
|
||||
```
|
||||
Format: {entry-id}_{document-type}_{uuid}.pdf
|
||||
|
||||
Examples:
|
||||
001_main_a7b3c9d2-4e5f-6789-abcd-ef0123456789.pdf
|
||||
001_exhibit_b8c4d0e3-5f60-789a-bcde-f01234567890.pdf
|
||||
002_main_c9d5e1f4-6071-890b-cdef-012345678901.pdf
|
||||
```
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
uploads/
|
||||
├── 2024/
|
||||
│ ├── 12/
|
||||
│ │ ├── 001_main_a7b3c9d2-4e5f-6789-abcd-ef0123456789.pdf
|
||||
│ │ ├── 001_exhibit_b8c4d0e3-5f60-789a-bcde-f01234567890.pdf
|
||||
│ │ └── 002_main_c9d5e1f4-6071-890b-cdef-012345678901.pdf
|
||||
│ └── 11/
|
||||
│ └── [previous month files]
|
||||
└── 2025/
|
||||
└── [future files]
|
||||
```
|
||||
|
||||
### File Upload Process
|
||||
1. Validate file type (PDF only) and size (max 10MB)
|
||||
2. Generate UUID for filename
|
||||
3. Create year/month directory structure
|
||||
4. Save file with UUID-based name
|
||||
5. Store metadata in database
|
||||
6. Return success response with document ID
|
||||
|
||||
## Frontend Components
|
||||
|
||||
### Public Interface Components
|
||||
```
|
||||
components/
|
||||
├── Layout/
|
||||
│ ├── Header.tsx # Site header with title and subscribe button
|
||||
│ ├── Footer.tsx # Site footer
|
||||
│ └── Layout.tsx # Main layout wrapper
|
||||
├── Docket/
|
||||
│ ├── DocketList.tsx # List of all docket entries
|
||||
│ ├── DocketEntry.tsx # Individual entry display
|
||||
│ ├── DocumentList.tsx # Documents within an entry
|
||||
│ └── DocumentViewer.tsx # PDF viewing modal
|
||||
├── Subscription/
|
||||
│ ├── SubscribeForm.tsx # Email subscription form
|
||||
│ └── SubscribeModal.tsx # Subscription confirmation modal
|
||||
└── Common/
|
||||
├── LoadingSpinner.tsx # Loading states
|
||||
├── ErrorMessage.tsx # Error display
|
||||
└── Button.tsx # Reusable button component
|
||||
```
|
||||
|
||||
### Admin Interface Components
|
||||
```
|
||||
components/admin/
|
||||
├── Auth/
|
||||
│ └── LoginForm.tsx # Admin login form
|
||||
├── Dashboard/
|
||||
│ ├── Dashboard.tsx # Main admin dashboard
|
||||
│ ├── EntryList.tsx # Manage existing entries
|
||||
│ └── Stats.tsx # Basic statistics
|
||||
├── Entry/
|
||||
│ ├── CreateEntry.tsx # New docket entry form
|
||||
│ ├── EditEntry.tsx # Edit existing entry
|
||||
│ └── DeleteEntry.tsx # Delete confirmation
|
||||
├── Document/
|
||||
│ ├── DocumentUpload.tsx # Multi-file upload component
|
||||
│ ├── DocumentEdit.tsx # Edit document metadata
|
||||
│ └── DocumentReplace.tsx # Replace PDF file
|
||||
└── Layout/
|
||||
├── AdminLayout.tsx # Admin layout wrapper
|
||||
└── AdminNav.tsx # Admin navigation
|
||||
```
|
||||
|
||||
## User Interface Layouts
|
||||
|
||||
### Public Docket Page
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ELIZA KRAGH v. MAD DOCKET │
|
||||
│ [Subscribe] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Case Information │
|
||||
│ ───────────────── │
|
||||
│ Case: Eliza Kragh v. Montana Association of the Deaf │
|
||||
│ Status: Ongoing │
|
||||
│ Last Updated: December 15, 2024 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Docket Entries │
|
||||
│ ───────────────── │
|
||||
│ [Date] | [Document Type] | [Summary] | [View] │
|
||||
│ ──────────────────────────────────────────────────────── │
|
||||
│ 12/15/24 | Motion to Dismiss | Defendant's motion... |[+] │
|
||||
│ └─ 📄 Main Document │
|
||||
│ └─ 📄 Exhibit A - Financial Records │
|
||||
│ └─ 📄 Exhibit B - Email Correspondence │
|
||||
│ ──────────────────────────────────────────────────────── │
|
||||
│ 12/10/24 | Complaint | Plaintiff's initial filing... |[+] │
|
||||
│ └─ 📄 Main Document │
|
||||
│ └─ 📄 Exhibit 1 - Contract Agreement │
|
||||
│ ──────────────────────────────────────────────────────── │
|
||||
│ [More entries...] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Admin Dashboard
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ADMIN DASHBOARD [Logout] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ [+ New Docket Entry] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Manage Existing Entries │
|
||||
│ ────────────────────── │
|
||||
│ 12/15/24 | Motion to Dismiss | [Edit] [Delete] │
|
||||
│ └─ 📄 Main Document [Edit] [Replace] [Del] │
|
||||
│ └─ 📄 Exhibit A [Edit] [Replace] [Del] │
|
||||
│ └─ 📄 Exhibit B [Edit] [Replace] [Del] │
|
||||
│ └─ [+ Add Document to Entry] │
|
||||
│ ──────────────────────────────────────────────────────── │
|
||||
│ 12/10/24 | Complaint | [Edit] [Delete] │
|
||||
│ └─ 📄 Main Document [Edit] [Replace] [Del] │
|
||||
│ └─ 📄 Exhibit 1 [Edit] [Replace] [Del] │
|
||||
│ └─ [+ Add Document to Entry] │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Docker Configuration
|
||||
|
||||
### docker-compose.yml
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=http://backend:3001/api
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "3001:3001"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://docket_user:docket_pass@postgres:5432/docket_db
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- JWT_SECRET=your-secret-key
|
||||
- UPLOAD_DIR=/app/uploads
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
|
||||
postgres:
|
||||
image: postgres:15
|
||||
environment:
|
||||
- POSTGRES_DB=docket_db
|
||||
- POSTGRES_USER=docket_user
|
||||
- POSTGRES_PASSWORD=docket_pass
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./nginx/ssl:/etc/nginx/ssl
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
uploads:
|
||||
```
|
||||
|
||||
## Security Implementation
|
||||
|
||||
### Authentication & Authorization
|
||||
- JWT tokens for admin authentication
|
||||
- Token expiration and refresh mechanism
|
||||
- Protected routes middleware
|
||||
- Role-based access control
|
||||
|
||||
### File Security
|
||||
- Files served through API endpoints only
|
||||
- No direct file system access
|
||||
- File type validation (PDF only)
|
||||
- File size limits (10MB max)
|
||||
- UUID-based naming prevents enumeration
|
||||
|
||||
### Input Validation
|
||||
- All API inputs validated with Joi/Zod
|
||||
- SQL injection prevention with Prisma ORM
|
||||
- XSS protection with input sanitization
|
||||
- CSRF protection for admin forms
|
||||
|
||||
### Rate Limiting
|
||||
- API endpoint rate limiting
|
||||
- File upload rate limiting
|
||||
- Login attempt limiting
|
||||
- Email subscription rate limiting
|
||||
|
||||
## Email Notification System
|
||||
|
||||
### Subscription Process
|
||||
1. User enters email on public site
|
||||
2. Email validated and stored in database
|
||||
3. Confirmation email sent with unsubscribe token
|
||||
4. User added to active subscribers list
|
||||
|
||||
### Notification Trigger
|
||||
1. Admin uploads new docket entry
|
||||
2. System checks for active subscribers
|
||||
3. Email notification queued for each subscriber
|
||||
4. Background job processes email queue
|
||||
5. Emails sent with entry summary and link
|
||||
|
||||
### Email Template
|
||||
```
|
||||
Subject: New Filing in Eliza Kragh v. Montana Association of the Deaf
|
||||
|
||||
Dear Subscriber,
|
||||
|
||||
A new document has been filed in the case of Eliza Kragh v. Montana Association of the Deaf:
|
||||
|
||||
Date: December 15, 2024
|
||||
Document Type: Motion to Dismiss
|
||||
Summary: Defendant's motion to dismiss based on lack of jurisdiction...
|
||||
|
||||
View the complete filing and all documents at:
|
||||
https://docket-site.com
|
||||
|
||||
To unsubscribe from these notifications, click here:
|
||||
https://docket-site.com/unsubscribe/[token]
|
||||
|
||||
Best regards,
|
||||
Court Docket Notification System
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Frontend Optimization
|
||||
- Next.js static generation for public pages
|
||||
- Image optimization for any graphics
|
||||
- Code splitting and lazy loading
|
||||
- PDF.js worker for document rendering
|
||||
- Caching strategies for API responses
|
||||
|
||||
### Backend Optimization
|
||||
- Database query optimization with indexes
|
||||
- Redis caching for frequently accessed data
|
||||
- File streaming for PDF downloads
|
||||
- Connection pooling for database
|
||||
- Compression middleware for API responses
|
||||
|
||||
### Infrastructure Optimization
|
||||
- Nginx reverse proxy with caching
|
||||
- Gzip compression for static assets
|
||||
- CDN integration for file serving
|
||||
- Database connection pooling
|
||||
- Redis session storage
|
||||
|
||||
## Deployment Strategy
|
||||
|
||||
### Development Environment
|
||||
- Docker Compose for local development
|
||||
- Hot reloading for both frontend and backend
|
||||
- Development database with sample data
|
||||
- Email testing with local SMTP server
|
||||
|
||||
### Production Environment
|
||||
- Docker Swarm or Kubernetes deployment
|
||||
- SSL/TLS certificates with Let's Encrypt
|
||||
- Production database with backups
|
||||
- Real SMTP service for email notifications
|
||||
- Monitoring and logging with ELK stack
|
||||
|
||||
### CI/CD Pipeline
|
||||
- GitHub Actions for automated testing
|
||||
- Docker image building and pushing
|
||||
- Automated deployment to staging
|
||||
- Manual approval for production deployment
|
||||
- Database migration automation
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Testing
|
||||
- Backend API endpoints with Jest
|
||||
- Frontend components with React Testing Library
|
||||
- Database operations with test database
|
||||
- File upload functionality testing
|
||||
|
||||
### Integration Testing
|
||||
- End-to-end user workflows with Cypress
|
||||
- API integration testing
|
||||
- Email notification testing
|
||||
- File serving and download testing
|
||||
|
||||
### Security Testing
|
||||
- Authentication and authorization testing
|
||||
- Input validation testing
|
||||
- File upload security testing
|
||||
- SQL injection prevention testing
|
||||
|
||||
This comprehensive design specification serves as the complete technical reference for implementing the Eliza Kragh v. MAD court docket website.
|
||||
297
README.md
297
README.md
|
|
@ -1,278 +1,59 @@
|
|||
# Elizabeth Kragh v. Montana Association of the Deaf - Court Docket Website
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
A public-facing website to display court documents and filings for the ongoing lawsuit: **Elizabeth Kragh v. Montana Association of the Deaf**. The system provides public transparency for court proceedings while allowing administrative management of documents and user notifications.
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## 🌐 Live Website
|
||||
## About Laravel
|
||||
|
||||
- **Public Access**: https://mad-lawsuit.org
|
||||
- **Admin Panel**: https://mad-lawsuit.org/admin
|
||||
- **Status**: LIVE AND OPERATIONAL ✅
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
## 🏗️ Architecture
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
- **Frontend**: Next.js 15.4.0 with React 19, TypeScript 6.0, Tailwind CSS
|
||||
- **Backend**: Node.js 22 LTS, Express.js 6.0, TypeScript 6.0, Prisma 5.0
|
||||
- **Database**: PostgreSQL 17.5 with Redis caching
|
||||
- **Infrastructure**: Docker Engine 28.1.1, Nginx reverse proxy
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## 🚀 Quick Start
|
||||
## Learning Laravel
|
||||
|
||||
### Prerequisites
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out [Laravel Learn](https://laravel.com/learn), where you will be guided through building a modern Laravel application.
|
||||
|
||||
- Node.js 22 LTS
|
||||
- Docker Engine 28.1.1 & Docker Compose
|
||||
- pnpm (recommended package manager)
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
### Development Setup
|
||||
## Laravel Sponsors
|
||||
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd eliza-mad-docket-website
|
||||
```
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
||||
|
||||
2. **Start the development environment**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
### Premium Partners
|
||||
|
||||
3. **Install dependencies**
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
pnpm install
|
||||
- **[Vehikl](https://vehikl.com)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel)**
|
||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
||||
- **[Redberry](https://redberry.international/laravel-development)**
|
||||
- **[Active Logic](https://activelogic.com)**
|
||||
|
||||
# Frontend
|
||||
cd ../frontend
|
||||
pnpm install
|
||||
```
|
||||
## Contributing
|
||||
|
||||
4. **Set up the database**
|
||||
```bash
|
||||
cd backend
|
||||
pnpm prisma migrate dev
|
||||
pnpm prisma generate
|
||||
```
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
5. **Start development servers**
|
||||
```bash
|
||||
# Backend (in backend directory)
|
||||
pnpm dev
|
||||
## Code of Conduct
|
||||
|
||||
# Frontend (in frontend directory)
|
||||
pnpm dev
|
||||
```
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
6. **Access the application**
|
||||
- Frontend: http://localhost:3000
|
||||
- Backend API: http://localhost:3001
|
||||
- Database: localhost:5432
|
||||
- Redis: localhost:6379
|
||||
## Security Vulnerabilities
|
||||
|
||||
## 📁 Project Structure
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
```
|
||||
eliza-mad-docket/
|
||||
├── backend/ # Express.js API
|
||||
│ ├── src/
|
||||
│ │ ├── routes/ # API routes
|
||||
│ │ ├── middleware/ # Express middleware
|
||||
│ │ ├── utils/ # Utility functions
|
||||
│ │ └── index.ts # Main server file
|
||||
│ ├── prisma/ # Database schema & migrations
|
||||
│ ├── Dockerfile # Backend container
|
||||
│ └── package.json
|
||||
├── frontend/ # Next.js application
|
||||
│ ├── src/
|
||||
│ │ ├── app/ # Next.js 13+ app directory
|
||||
│ │ ├── components/ # React components
|
||||
│ │ └── lib/ # Utility libraries
|
||||
│ ├── Dockerfile # Frontend container
|
||||
│ └── package.json
|
||||
├── cline_docs/ # Memory Bank documentation
|
||||
├── docker-compose.yml # Container orchestration
|
||||
└── DESIGN_SPECIFICATION.md # Complete technical spec
|
||||
```
|
||||
## License
|
||||
|
||||
## 🔧 Environment Variables
|
||||
|
||||
### Backend (.env)
|
||||
```env
|
||||
DATABASE_URL=postgresql://docket_user:docket_pass@localhost:5432/docket_db
|
||||
REDIS_URL=redis://localhost:6379
|
||||
JWT_SECRET=your-secret-key-change-in-production
|
||||
JWT_EXPIRES_IN=24h
|
||||
UPLOAD_DIR=/app/uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
NODE_ENV=development
|
||||
```
|
||||
|
||||
### Frontend (.env.local)
|
||||
```env
|
||||
NEXT_PUBLIC_API_URL=http://localhost:3001/api
|
||||
```
|
||||
|
||||
## 🗄️ Database Schema
|
||||
|
||||
The application uses PostgreSQL with the following main tables:
|
||||
|
||||
- **docket_entries**: Main court filings
|
||||
- **documents**: PDF files associated with entries
|
||||
- **subscriptions**: Email subscriptions for notifications
|
||||
- **admin_users**: Administrative users
|
||||
|
||||
## 🔐 Security Features
|
||||
|
||||
- JWT authentication for admin access
|
||||
- File access control through API endpoints
|
||||
- UUID-based file naming for security
|
||||
- Input validation and sanitization
|
||||
- Rate limiting on API endpoints
|
||||
- CORS protection
|
||||
|
||||
## 📄 File Management
|
||||
|
||||
- **Storage**: Local filesystem with Docker volumes
|
||||
- **Naming**: UUID-based scheme: `{entry-id}_{document-type}_{uuid}.pdf`
|
||||
- **Structure**: Organized by year/month directories
|
||||
- **Security**: Files served only through API endpoints
|
||||
- **Limits**: 10MB maximum file size, PDF only
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
### Production Deployment
|
||||
|
||||
For production deployment with Portainer and Caddy, see the complete [DEPLOYMENT.md](./DEPLOYMENT.md) guide.
|
||||
|
||||
**Quick Production Setup:**
|
||||
|
||||
1. **Prepare environment**
|
||||
```bash
|
||||
cp .env.prod .env
|
||||
# Edit .env with secure values
|
||||
```
|
||||
|
||||
2. **Deploy with production compose**
|
||||
```bash
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
```
|
||||
|
||||
3. **Set up admin user for Elizabeth**
|
||||
```bash
|
||||
docker exec -it backend_container node setup-admin-fixed.js
|
||||
```
|
||||
|
||||
## 🔐 Admin Access
|
||||
|
||||
### Current Production Credentials
|
||||
- **URL**: https://mad-lawsuit.org/admin
|
||||
- **Username**: `eliza`
|
||||
- **Password**: `AXEoZWk2AMAD0naw`
|
||||
- **Status**: Active and functional ✅
|
||||
|
||||
**⚠️ Security Note**: These credentials were generated during production deployment. Change them immediately after first login for enhanced security.
|
||||
|
||||
### Development Setup
|
||||
|
||||
1. **Build containers**
|
||||
```bash
|
||||
docker-compose build
|
||||
```
|
||||
|
||||
2. **Run development stack**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
3. **Run database migrations**
|
||||
```bash
|
||||
docker-compose exec backend pnpm prisma migrate deploy
|
||||
```
|
||||
|
||||
### Environment-specific configurations
|
||||
|
||||
- **Development**: Hot reloading, detailed logging, demo credentials
|
||||
- **Production**: Optimized builds, security headers, SSL/TLS, secure credentials
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
```bash
|
||||
# Backend tests
|
||||
cd backend
|
||||
pnpm test
|
||||
|
||||
# Frontend tests
|
||||
cd frontend
|
||||
pnpm test
|
||||
|
||||
# Type checking
|
||||
pnpm type-check
|
||||
```
|
||||
|
||||
## 📊 API Endpoints
|
||||
|
||||
### Public Endpoints
|
||||
- `GET /api/docket-entries` - Get all docket entries with documents
|
||||
- `GET /api/documents/:id/download` - Download PDF file
|
||||
- `POST /api/subscribe` - Subscribe to email notifications
|
||||
- `GET /api/unsubscribe/:token` - Unsubscribe from notifications
|
||||
|
||||
### Admin Endpoints (Protected)
|
||||
- `POST /api/auth/login` - Admin authentication
|
||||
- `POST /api/docket-entries` - Create new docket entry
|
||||
- `PUT /api/docket-entries/:id` - Update docket entry
|
||||
- `DELETE /api/docket-entries/:id` - Delete docket entry
|
||||
- `POST /api/documents` - Upload document
|
||||
- `PUT /api/documents/:id` - Update document metadata
|
||||
- `DELETE /api/documents/:id` - Delete document
|
||||
|
||||
## 🎨 UI Features
|
||||
|
||||
### Public Interface
|
||||
- Clean, responsive design
|
||||
- Document viewing with PDF.js
|
||||
- Email subscription system
|
||||
- Mobile-friendly layout
|
||||
- Accessibility compliant
|
||||
|
||||
### Admin Interface
|
||||
- Secure login system
|
||||
- Multi-file upload support
|
||||
- Document management (CRUD operations)
|
||||
- File replacement capability
|
||||
- Metadata editing
|
||||
|
||||
## 📧 Email Notifications
|
||||
|
||||
- Automatic notifications for new filings
|
||||
- Unsubscribe functionality
|
||||
- HTML email templates
|
||||
- Background job processing
|
||||
|
||||
## 🔍 Monitoring & Logging
|
||||
|
||||
- Structured logging with Winston
|
||||
- Request/response logging
|
||||
- Error tracking and reporting
|
||||
- Health check endpoints
|
||||
- Performance monitoring
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Add tests if applicable
|
||||
5. Submit a pull request
|
||||
|
||||
## 📝 License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For questions or issues, please contact the development team or create an issue in the repository.
|
||||
|
||||
---
|
||||
|
||||
**Note**: This is a public transparency tool for court proceedings. All uploaded documents become publicly accessible through this website.
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
|
|
|||
52
app/Http/Controllers/Auth/AuthenticatedSessionController.php
Normal file
52
app/Http/Controllers/Auth/AuthenticatedSessionController.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class AuthenticatedSessionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the login view.
|
||||
*/
|
||||
public function create(): Response
|
||||
{
|
||||
return Inertia::render('Auth/Login', [
|
||||
'canResetPassword' => Route::has('password.request'),
|
||||
'status' => session('status'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming authentication request.
|
||||
*/
|
||||
public function store(LoginRequest $request): RedirectResponse
|
||||
{
|
||||
$request->authenticate();
|
||||
|
||||
$request->session()->regenerate();
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy an authenticated session.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::guard('web')->logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
41
app/Http/Controllers/Auth/ConfirmablePasswordController.php
Normal file
41
app/Http/Controllers/Auth/ConfirmablePasswordController.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class ConfirmablePasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the confirm password view.
|
||||
*/
|
||||
public function show(): Response
|
||||
{
|
||||
return Inertia::render('Auth/ConfirmPassword');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the user's password.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
if (! Auth::guard('web')->validate([
|
||||
'email' => $request->user()->email,
|
||||
'password' => $request->password,
|
||||
])) {
|
||||
throw ValidationException::withMessages([
|
||||
'password' => __('auth.password'),
|
||||
]);
|
||||
}
|
||||
|
||||
$request->session()->put('auth.password_confirmed_at', time());
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class EmailVerificationNotificationController extends Controller
|
||||
{
|
||||
/**
|
||||
* Send a new email verification notification.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false));
|
||||
}
|
||||
|
||||
$request->user()->sendEmailVerificationNotification();
|
||||
|
||||
return back()->with('status', 'verification-link-sent');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class EmailVerificationPromptController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the email verification prompt.
|
||||
*/
|
||||
public function __invoke(Request $request): RedirectResponse|Response
|
||||
{
|
||||
return $request->user()->hasVerifiedEmail()
|
||||
? redirect()->intended(route('dashboard', absolute: false))
|
||||
: Inertia::render('Auth/VerifyEmail', ['status' => session('status')]);
|
||||
}
|
||||
}
|
||||
69
app/Http/Controllers/Auth/NewPasswordController.php
Normal file
69
app/Http/Controllers/Auth/NewPasswordController.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class NewPasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the password reset view.
|
||||
*/
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
return Inertia::render('Auth/ResetPassword', [
|
||||
'email' => $request->email,
|
||||
'token' => $request->route('token'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming new password request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'token' => 'required',
|
||||
'email' => 'required|email',
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
// Here we will attempt to reset the user's password. If it is successful we
|
||||
// will update the password on an actual user model and persist it to the
|
||||
// database. Otherwise we will parse the error and return the response.
|
||||
$status = Password::reset(
|
||||
$request->only('email', 'password', 'password_confirmation', 'token'),
|
||||
function ($user) use ($request) {
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($request->password),
|
||||
'remember_token' => Str::random(60),
|
||||
])->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
);
|
||||
|
||||
// If the password was successfully reset, we will redirect the user back to
|
||||
// the application's home authenticated view. If there is an error we can
|
||||
// redirect them back to where they came from with their error message.
|
||||
if ($status == Password::PASSWORD_RESET) {
|
||||
return redirect()->route('login')->with('status', __($status));
|
||||
}
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => [trans($status)],
|
||||
]);
|
||||
}
|
||||
}
|
||||
29
app/Http/Controllers/Auth/PasswordController.php
Normal file
29
app/Http/Controllers/Auth/PasswordController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/**
|
||||
* Update the user's password.
|
||||
*/
|
||||
public function update(Request $request): RedirectResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'current_password' => ['required', 'current_password'],
|
||||
'password' => ['required', Password::defaults(), 'confirmed'],
|
||||
]);
|
||||
|
||||
$request->user()->update([
|
||||
'password' => Hash::make($validated['password']),
|
||||
]);
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
51
app/Http/Controllers/Auth/PasswordResetLinkController.php
Normal file
51
app/Http/Controllers/Auth/PasswordResetLinkController.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class PasswordResetLinkController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the password reset link request view.
|
||||
*/
|
||||
public function create(): Response
|
||||
{
|
||||
return Inertia::render('Auth/ForgotPassword', [
|
||||
'status' => session('status'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming password reset link request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'email' => 'required|email',
|
||||
]);
|
||||
|
||||
// We will send the password reset link to this user. Once we have attempted
|
||||
// to send the link, we will examine the response then see the message we
|
||||
// need to show to the user. Finally, we'll send out a proper response.
|
||||
$status = Password::sendResetLink(
|
||||
$request->only('email')
|
||||
);
|
||||
|
||||
if ($status == Password::RESET_LINK_SENT) {
|
||||
return back()->with('status', __($status));
|
||||
}
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => [trans($status)],
|
||||
]);
|
||||
}
|
||||
}
|
||||
51
app/Http/Controllers/Auth/RegisteredUserController.php
Normal file
51
app/Http/Controllers/Auth/RegisteredUserController.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class RegisteredUserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the registration view.
|
||||
*/
|
||||
public function create(): Response
|
||||
{
|
||||
return Inertia::render('Auth/Register');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming registration request.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|lowercase|email|max:255|unique:'.User::class,
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
]);
|
||||
|
||||
event(new Registered($user));
|
||||
|
||||
Auth::login($user);
|
||||
|
||||
return redirect(route('dashboard', absolute: false));
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
27
app/Http/Controllers/Auth/VerifyEmailController.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Auth\Events\Verified;
|
||||
use Illuminate\Foundation\Auth\EmailVerificationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class VerifyEmailController extends Controller
|
||||
{
|
||||
/**
|
||||
* Mark the authenticated user's email address as verified.
|
||||
*/
|
||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->user()->hasVerifiedEmail()) {
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
|
||||
if ($request->user()->markEmailAsVerified()) {
|
||||
event(new Verified($request->user()));
|
||||
}
|
||||
|
||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
||||
}
|
||||
}
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
29
app/Http/Controllers/HomeController.php
Normal file
29
app/Http/Controllers/HomeController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\DocketEntry;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the home page with all docket entries.
|
||||
*/
|
||||
public function index(): Response
|
||||
{
|
||||
$docketEntries = DocketEntry::with('documents')
|
||||
->orderBy('date', 'desc')
|
||||
->get();
|
||||
|
||||
return Inertia::render('Home', [
|
||||
'docketEntries' => $docketEntries,
|
||||
'caseInfo' => [
|
||||
'title' => 'Elizabeth Kragh v. Montana Association of the Deaf',
|
||||
'status' => 'Active Litigation',
|
||||
'lastUpdated' => $docketEntries->first()?->date?->format('F j, Y') ?? 'No entries yet',
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
63
app/Http/Controllers/ProfileController.php
Normal file
63
app/Http/Controllers/ProfileController.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user's profile form.
|
||||
*/
|
||||
public function edit(Request $request): Response
|
||||
{
|
||||
return Inertia::render('Profile/Edit', [
|
||||
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
|
||||
'status' => session('status'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*/
|
||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$request->user()->fill($request->validated());
|
||||
|
||||
if ($request->user()->isDirty('email')) {
|
||||
$request->user()->email_verified_at = null;
|
||||
}
|
||||
|
||||
$request->user()->save();
|
||||
|
||||
return Redirect::route('profile.edit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the user's account.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'password' => ['required', 'current_password'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$user->delete();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
}
|
||||
39
app/Http/Middleware/HandleInertiaRequests.php
Normal file
39
app/Http/Middleware/HandleInertiaRequests.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
/**
|
||||
* The root template that is loaded on the first page visit.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rootView = 'app';
|
||||
|
||||
/**
|
||||
* Determine the current asset version.
|
||||
*/
|
||||
public function version(Request $request): ?string
|
||||
{
|
||||
return parent::version($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the props that are shared by default.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function share(Request $request): array
|
||||
{
|
||||
return [
|
||||
...parent::share($request),
|
||||
'auth' => [
|
||||
'user' => $request->user(),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
85
app/Http/Requests/Auth/LoginRequest.php
Normal file
85
app/Http/Requests/Auth/LoginRequest.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'string', 'email'],
|
||||
'password' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the request's credentials.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function authenticate(): void
|
||||
{
|
||||
$this->ensureIsNotRateLimited();
|
||||
|
||||
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.failed'),
|
||||
]);
|
||||
}
|
||||
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the login request is not rate limited.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function ensureIsNotRateLimited(): void
|
||||
{
|
||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event(new Lockout($this));
|
||||
|
||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.throttle', [
|
||||
'seconds' => $seconds,
|
||||
'minutes' => ceil($seconds / 60),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate limiting throttle key for the request.
|
||||
*/
|
||||
public function throttleKey(): string
|
||||
{
|
||||
return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
|
||||
}
|
||||
}
|
||||
30
app/Http/Requests/ProfileUpdateRequest.php
Normal file
30
app/Http/Requests/ProfileUpdateRequest.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ProfileUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'lowercase',
|
||||
'email',
|
||||
'max:255',
|
||||
Rule::unique(User::class)->ignore($this->user()->id),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
34
app/Models/AdminUser.php
Normal file
34
app/Models/AdminUser.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AdminUser extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'username',
|
||||
'password',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* Automatically hash password when setting.
|
||||
*/
|
||||
public function setPasswordAttribute($value)
|
||||
{
|
||||
$this->attributes['password'] = Hash::make($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify password.
|
||||
*/
|
||||
public function verifyPassword($password): bool
|
||||
{
|
||||
return Hash::check($password, $this->password);
|
||||
}
|
||||
}
|
||||
28
app/Models/DocketEntry.php
Normal file
28
app/Models/DocketEntry.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class DocketEntry extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'date',
|
||||
'title',
|
||||
'summary',
|
||||
'notes',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'date' => 'date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the documents for the docket entry.
|
||||
*/
|
||||
public function documents(): HasMany
|
||||
{
|
||||
return $this->hasMany(Document::class)->orderBy('display_order');
|
||||
}
|
||||
}
|
||||
35
app/Models/Document.php
Normal file
35
app/Models/Document.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Document extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'docket_entry_id',
|
||||
'title',
|
||||
'original_filename',
|
||||
'stored_filename',
|
||||
'file_path',
|
||||
'file_size',
|
||||
'mime_type',
|
||||
'summary',
|
||||
'notes',
|
||||
'display_order',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'file_size' => 'integer',
|
||||
'display_order' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the docket entry that owns the document.
|
||||
*/
|
||||
public function docketEntry(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(DocketEntry::class);
|
||||
}
|
||||
}
|
||||
33
app/Models/Subscription.php
Normal file
33
app/Models/Subscription.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Subscription extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'email',
|
||||
'is_active',
|
||||
'unsubscribe_token',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Generate a unique unsubscribe token.
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function ($subscription) {
|
||||
if (empty($subscription->unsubscribe_token)) {
|
||||
$subscription->unsubscribe_token = Str::random(64);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
48
app/Models/User.php
Normal file
48
app/Models/User.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
25
app/Providers/AppServiceProvider.php
Normal file
25
app/Providers/AppServiceProvider.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Vite::prefetch(concurrency: 3);
|
||||
}
|
||||
}
|
||||
18
artisan
Executable file
18
artisan
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
// Bootstrap Laravel and handle the command...
|
||||
/** @var Application $app */
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
$status = $app->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
||||
28
backend/.env
28
backend/.env
|
|
@ -1,28 +0,0 @@
|
|||
# Database
|
||||
DATABASE_URL=postgresql://docket_user:docket_pass@localhost:5432/docket_db
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Email Configuration (Gmail Smuggler)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=2525
|
||||
SMTP_SECURE=false
|
||||
SMTP_USER=system@deafgain.org
|
||||
SMTP_PASS=ojvlysraxwjriwzy
|
||||
FROM_EMAIL=system@deafgain.org
|
||||
RECIPIENT_EMAIL=eliza@deafgain.org
|
||||
GOOGLE_EMAIL=system@deafgain.org
|
||||
GOOGLE_APP_PASSWORD=ojvlysraxwjriwzy
|
||||
|
||||
# Authentication
|
||||
JWT_SECRET=your-secret-key-change-in-production
|
||||
JWT_EXPIRES_IN=24h
|
||||
|
||||
# File Upload
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
|
||||
# Environment
|
||||
NODE_ENV=development
|
||||
PORT=3001
|
||||
LOG_LEVEL=info
|
||||
FRONTEND_URL=https://mad-lawsuit.org
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
# Use Node.js 22 Alpine image to match package.json engines requirement
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add --no-cache openssl curl python3 make g++
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copy package files
|
||||
COPY package.json pnpm-lock.yaml* ./
|
||||
|
||||
# Install all dependencies
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copy Prisma schema
|
||||
COPY prisma ./prisma/
|
||||
|
||||
# Generate Prisma client
|
||||
RUN pnpm prisma generate
|
||||
|
||||
# Copy source code and build
|
||||
COPY . .
|
||||
RUN pnpm build
|
||||
|
||||
# Remove dev dependencies to reduce image size
|
||||
ENV CI=true
|
||||
RUN pnpm prune --prod
|
||||
|
||||
# Generate Prisma client after prune (prisma CLI now available in production deps)
|
||||
RUN pnpm prisma generate
|
||||
|
||||
# Create uploads directory
|
||||
RUN mkdir -p /app/uploads
|
||||
|
||||
# Set environment variables
|
||||
ENV NODE_ENV=production
|
||||
ENV PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3001
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:3001/api/health || exit 1
|
||||
|
||||
# Start the application
|
||||
CMD ["pnpm", "start"]
|
||||
130
backend/dist/index.js
vendored
130
backend/dist/index.js
vendored
|
|
@ -1,130 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.redis = exports.prisma = void 0;
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const cors_1 = __importDefault(require("cors"));
|
||||
const helmet_1 = __importDefault(require("helmet"));
|
||||
const compression_1 = __importDefault(require("compression"));
|
||||
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
||||
const dotenv_1 = __importDefault(require("dotenv"));
|
||||
const client_1 = require("@prisma/client");
|
||||
const adapter_pg_1 = require("@prisma/adapter-pg");
|
||||
const pg_1 = __importDefault(require("pg"));
|
||||
const redis_1 = require("redis");
|
||||
const logger_1 = require("./utils/logger");
|
||||
const errorHandler_1 = require("./middleware/errorHandler");
|
||||
const requestLogger_1 = require("./middleware/requestLogger");
|
||||
const auth_1 = __importDefault(require("./routes/auth"));
|
||||
const docket_1 = __importDefault(require("./routes/docket"));
|
||||
const documents_1 = __importDefault(require("./routes/documents"));
|
||||
const subscriptions_1 = __importDefault(require("./routes/subscriptions"));
|
||||
const health_1 = __importDefault(require("./routes/health"));
|
||||
const notifications_1 = __importDefault(require("./routes/notifications"));
|
||||
dotenv_1.default.config();
|
||||
const app = (0, express_1.default)();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
app.set('trust proxy', true);
|
||||
const connectionString = process.env.DATABASE_URL;
|
||||
const pool = new pg_1.default.Pool({ connectionString });
|
||||
const adapter = new adapter_pg_1.PrismaPg(pool);
|
||||
exports.prisma = new client_1.PrismaClient({
|
||||
adapter,
|
||||
log: ['query', 'info', 'warn', 'error'],
|
||||
});
|
||||
exports.redis = (0, redis_1.createClient)({
|
||||
url: process.env.REDIS_URL || 'redis://localhost:6379',
|
||||
});
|
||||
exports.redis.connect().catch((error) => {
|
||||
logger_1.logger.error('Redis connection failed:', error);
|
||||
});
|
||||
exports.redis.on('error', (error) => {
|
||||
logger_1.logger.error('Redis error:', error);
|
||||
});
|
||||
exports.redis.on('connect', () => {
|
||||
logger_1.logger.info('Connected to Redis');
|
||||
});
|
||||
app.use((0, helmet_1.default)({
|
||||
crossOriginResourcePolicy: { policy: "cross-origin" }
|
||||
}));
|
||||
app.use((0, cors_1.default)({
|
||||
origin: process.env.NODE_ENV === 'production'
|
||||
? [
|
||||
process.env.FRONTEND_URL,
|
||||
'https://mad-lawsuit.org',
|
||||
'https://files.mad-lawsuit.org',
|
||||
'http://frontend:806',
|
||||
'http://localhost:806'
|
||||
]
|
||||
: [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:806',
|
||||
'http://frontend:806',
|
||||
'http://frontend:3000',
|
||||
'https://mad-lawsuit.org',
|
||||
'https://files.mad-lawsuit.org'
|
||||
],
|
||||
credentials: true,
|
||||
}));
|
||||
const limiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 100,
|
||||
message: 'Too many requests from this IP, please try again later.',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
skip: (req) => {
|
||||
return false;
|
||||
},
|
||||
keyGenerator: (req) => {
|
||||
return req.ip || 'unknown';
|
||||
},
|
||||
});
|
||||
app.use('/api', limiter);
|
||||
const authLimiter = (0, express_rate_limit_1.default)({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 5,
|
||||
message: 'Too many authentication attempts, please try again later.',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req) => {
|
||||
return req.ip || 'unknown';
|
||||
},
|
||||
});
|
||||
app.use('/api/auth', authLimiter);
|
||||
app.use((0, compression_1.default)());
|
||||
app.use(express_1.default.json({ limit: '10mb' }));
|
||||
app.use(express_1.default.urlencoded({ extended: true, limit: '10mb' }));
|
||||
app.use(requestLogger_1.requestLogger);
|
||||
app.use('/api/health', health_1.default);
|
||||
app.use('/api/auth', auth_1.default);
|
||||
app.use('/api/docket-entries', docket_1.default);
|
||||
app.use('/api/documents', documents_1.default);
|
||||
app.use('/api/subscriptions', subscriptions_1.default);
|
||||
app.use('/api/notifications', notifications_1.default);
|
||||
app.use('*', (req, res) => {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: 'Route not found',
|
||||
});
|
||||
});
|
||||
app.use(errorHandler_1.errorHandler);
|
||||
process.on('SIGTERM', async () => {
|
||||
logger_1.logger.info('SIGTERM received, shutting down gracefully');
|
||||
await exports.prisma.$disconnect();
|
||||
await exports.redis.quit();
|
||||
process.exit(0);
|
||||
});
|
||||
process.on('SIGINT', async () => {
|
||||
logger_1.logger.info('SIGINT received, shutting down gracefully');
|
||||
await exports.prisma.$disconnect();
|
||||
await exports.redis.quit();
|
||||
process.exit(0);
|
||||
});
|
||||
app.listen(PORT, () => {
|
||||
logger_1.logger.info(`Server running on port ${PORT}`);
|
||||
logger_1.logger.info(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
||||
});
|
||||
exports.default = app;
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
backend/dist/index.js.map
vendored
1
backend/dist/index.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA8B;AAC9B,gDAAwB;AACxB,oDAA4B;AAC5B,8DAAsC;AACtC,4EAA2C;AAC3C,oDAA4B;AAC5B,2CAA8C;AAC9C,mDAA8C;AAC9C,4CAAoB;AACpB,iCAAqC;AACrC,2CAAwC;AACxC,4DAAyD;AACzD,8DAA2D;AAG3D,yDAAuC;AACvC,6DAA2C;AAC3C,mEAAgD;AAChD,2EAAwD;AACxD,6DAA2C;AAC3C,2EAAwD;AAGxD,gBAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;AAGtC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;AAG7B,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAClD,MAAM,IAAI,GAAG,IAAI,YAAE,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAC/C,MAAM,OAAO,GAAG,IAAI,qBAAQ,CAAC,IAAI,CAAC,CAAC;AAEtB,QAAA,MAAM,GAAG,IAAI,qBAAY,CAAC;IACrC,OAAO;IACP,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;CACxC,CAAC,CAAC;AAGU,QAAA,KAAK,GAAG,IAAA,oBAAY,EAAC;IAChC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,wBAAwB;CACvD,CAAC,CAAC;AAGH,aAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC9B,eAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,aAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;IAC1B,eAAM,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,aAAK,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,eAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AACpC,CAAC,CAAC,CAAC;AAGH,GAAG,CAAC,GAAG,CAAC,IAAA,gBAAM,EAAC;IACb,yBAAyB,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE;CACtD,CAAC,CAAC,CAAC;AAGJ,GAAG,CAAC,GAAG,CAAC,IAAA,cAAI,EAAC;IACX,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC3C,CAAC,CAAC;YACE,OAAO,CAAC,GAAG,CAAC,YAAY;YACxB,yBAAyB;YACzB,+BAA+B;YAC/B,qBAAqB;YACrB,sBAAsB;SACvB;QACH,CAAC,CAAC;YACE,uBAAuB;YACvB,sBAAsB;YACtB,qBAAqB;YACrB,sBAAsB;YACtB,yBAAyB;YACzB,+BAA+B;SAChC;IACL,WAAW,EAAE,IAAI;CAClB,CAAC,CAAC,CAAC;AAGJ,MAAM,OAAO,GAAG,IAAA,4BAAS,EAAC;IACxB,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IACxB,GAAG,EAAE,GAAG;IACR,OAAO,EAAE,yDAAyD;IAClE,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,KAAK;IAEpB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;QAEZ,OAAO,KAAK,CAAC;IACf,CAAC;IACD,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;QAEpB,OAAO,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;IAC7B,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAGzB,MAAM,WAAW,GAAG,IAAA,4BAAS,EAAC;IAC5B,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IACxB,GAAG,EAAE,CAAC;IACN,OAAO,EAAE,2DAA2D;IACpE,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,KAAK;IACpB,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;QACpB,OAAO,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;IAC7B,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAGlC,GAAG,CAAC,GAAG,CAAC,IAAA,qBAAW,GAAE,CAAC,CAAC;AACvB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AACzC,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAG/D,GAAG,CAAC,GAAG,CAAC,6BAAa,CAAC,CAAC;AAGvB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,gBAAY,CAAC,CAAC;AACrC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,cAAU,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,gBAAY,CAAC,CAAC;AAC7C,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,mBAAc,CAAC,CAAC;AAC1C,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,uBAAkB,CAAC,CAAC;AAClD,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,uBAAkB,CAAC,CAAC;AAGlD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACxB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACnB,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,iBAAiB;KAC3B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAGH,GAAG,CAAC,GAAG,CAAC,2BAAY,CAAC,CAAC;AAGtB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;IAC/B,eAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAG1D,MAAM,cAAM,CAAC,WAAW,EAAE,CAAC;IAG3B,MAAM,aAAK,CAAC,IAAI,EAAE,CAAC;IAEnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;IAC9B,eAAM,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAGzD,MAAM,cAAM,CAAC,WAAW,EAAE,CAAC;IAG3B,MAAM,aAAK,CAAC,IAAI,EAAE,CAAC;IAEnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAGH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,eAAM,CAAC,IAAI,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IAC9C,eAAM,CAAC,IAAI,CAAC,gBAAgB,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,EAAE,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,kBAAe,GAAG,CAAC"}
|
||||
50
backend/dist/middleware/auth.js
vendored
50
backend/dist/middleware/auth.js
vendored
|
|
@ -1,50 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.authenticateToken = void 0;
|
||||
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
||||
const index_1 = require("../index");
|
||||
const authenticateToken = async (req, res, next) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Access token required',
|
||||
});
|
||||
}
|
||||
try {
|
||||
const jwtSecret = process.env['JWT_SECRET'];
|
||||
if (!jwtSecret) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Server configuration error',
|
||||
});
|
||||
}
|
||||
const decoded = jsonwebtoken_1.default.verify(token, jwtSecret);
|
||||
const user = await index_1.prisma.adminUser.findUnique({
|
||||
where: { id: decoded.userId },
|
||||
});
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid token - user not found',
|
||||
});
|
||||
}
|
||||
req.user = {
|
||||
userId: decoded.userId,
|
||||
username: decoded.username,
|
||||
};
|
||||
next();
|
||||
}
|
||||
catch (error) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Invalid or expired token',
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.authenticateToken = authenticateToken;
|
||||
//# sourceMappingURL=auth.js.map
|
||||
1
backend/dist/middleware/auth.js.map
vendored
1
backend/dist/middleware/auth.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":";;;;;;AACA,gEAA+B;AAC/B,oCAAkC;AAS3B,MAAM,iBAAiB,GAAG,KAAK,EACpC,GAAyB,EACzB,GAAa,EACb,IAAkB,EAClB,EAAE;IACF,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,UAAU,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAErD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,4BAA4B;aACtC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,CAAQ,CAAC;QAGpD,MAAM,IAAI,GAAG,MAAM,cAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,EAAE;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,gCAAgC;aAC1C,CAAC,CAAC;QACL,CAAC;QAED,GAAG,CAAC,IAAI,GAAG;YACT,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B,CAAC;QAEF,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,0BAA0B;SACpC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC;AAlDW,QAAA,iBAAiB,qBAkD5B"}
|
||||
37
backend/dist/middleware/errorHandler.js
vendored
37
backend/dist/middleware/errorHandler.js
vendored
|
|
@ -1,37 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.asyncHandler = exports.createError = exports.errorHandler = void 0;
|
||||
const logger_1 = require("../utils/logger");
|
||||
const errorHandler = (error, req, res, next) => {
|
||||
const statusCode = error.statusCode || 500;
|
||||
const message = error.message || 'Internal Server Error';
|
||||
logger_1.logger.error('Error occurred:', {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
});
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
message: isDevelopment ? message : 'Something went wrong',
|
||||
...(isDevelopment && { stack: error.stack }),
|
||||
});
|
||||
};
|
||||
exports.errorHandler = errorHandler;
|
||||
const createError = (message, statusCode = 500) => {
|
||||
const error = new Error(message);
|
||||
error.statusCode = statusCode;
|
||||
error.isOperational = true;
|
||||
return error;
|
||||
};
|
||||
exports.createError = createError;
|
||||
const asyncHandler = (fn) => {
|
||||
return (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
};
|
||||
exports.asyncHandler = asyncHandler;
|
||||
//# sourceMappingURL=errorHandler.js.map
|
||||
1
backend/dist/middleware/errorHandler.js.map
vendored
1
backend/dist/middleware/errorHandler.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":";;;AACA,4CAAyC;AAOlC,MAAM,YAAY,GAAG,CAC1B,KAAe,EACf,GAAY,EACZ,GAAa,EACb,IAAkB,EACZ,EAAE;IACR,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,uBAAuB,CAAC;IAGzD,eAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE;QAC9B,KAAK,EAAE,KAAK,CAAC,OAAO;QACpB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;KACjC,CAAC,CAAC;IAGH,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;IAE7D,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;QAC1B,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB;QACzD,GAAG,CAAC,aAAa,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;KAC7C,CAAC,CAAC;AACL,CAAC,CAAC;AA3BW,QAAA,YAAY,gBA2BvB;AAEK,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,aAAqB,GAAG,EAAY,EAAE;IACjF,MAAM,KAAK,GAAa,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;IAC9B,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;IAC3B,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AALW,QAAA,WAAW,eAKtB;AAEK,MAAM,YAAY,GAAG,CAAC,EAAY,EAAE,EAAE;IAC3C,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC,CAAC;AAJW,QAAA,YAAY,gBAIvB"}
|
||||
28
backend/dist/middleware/requestLogger.js
vendored
28
backend/dist/middleware/requestLogger.js
vendored
|
|
@ -1,28 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.requestLogger = void 0;
|
||||
const logger_1 = require("../utils/logger");
|
||||
const requestLogger = (req, res, next) => {
|
||||
const start = Date.now();
|
||||
logger_1.logger.info('Incoming request', {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
});
|
||||
const originalEnd = res.end;
|
||||
res.end = function (chunk, encoding) {
|
||||
const duration = Date.now() - start;
|
||||
logger_1.logger.info('Request completed', {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
statusCode: res.statusCode,
|
||||
duration: `${duration}ms`,
|
||||
ip: req.ip,
|
||||
});
|
||||
return originalEnd.call(this, chunk, encoding);
|
||||
};
|
||||
next();
|
||||
};
|
||||
exports.requestLogger = requestLogger;
|
||||
//# sourceMappingURL=requestLogger.js.map
|
||||
1
backend/dist/middleware/requestLogger.js.map
vendored
1
backend/dist/middleware/requestLogger.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"requestLogger.js","sourceRoot":"","sources":["../../src/middleware/requestLogger.ts"],"names":[],"mappings":";;;AACA,4CAAyC;AAElC,MAAM,aAAa,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;IACrF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAGzB,eAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;QAC9B,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;KACjC,CAAC,CAAC;IAGH,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC;IAC5B,GAAG,CAAC,GAAG,GAAG,UAAS,KAAW,EAAE,QAAc;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;QAEpC,eAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;YAC/B,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,QAAQ,EAAE,GAAG,QAAQ,IAAI;YACzB,EAAE,EAAE,GAAG,CAAC,EAAE;SACX,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAQ,CAAC;IAET,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AA5BW,QAAA,aAAa,iBA4BxB"}
|
||||
85
backend/dist/routes/auth.js
vendored
85
backend/dist/routes/auth.js
vendored
|
|
@ -1,85 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
||||
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
||||
const index_1 = require("../index");
|
||||
const errorHandler_1 = require("../middleware/errorHandler");
|
||||
const auth_1 = require("../middleware/auth");
|
||||
const joi_1 = __importDefault(require("joi"));
|
||||
const router = (0, express_1.Router)();
|
||||
const loginSchema = joi_1.default.object({
|
||||
username: joi_1.default.string().required(),
|
||||
password: joi_1.default.string().required(),
|
||||
});
|
||||
router.post('/login', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const { error, value } = loginSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
const { username, password } = value;
|
||||
const admin = await index_1.prisma.adminUser.findUnique({
|
||||
where: { username },
|
||||
});
|
||||
if (!admin) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid credentials',
|
||||
});
|
||||
}
|
||||
const isValidPassword = await bcryptjs_1.default.compare(password, admin.passwordHash);
|
||||
if (!isValidPassword) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid credentials',
|
||||
});
|
||||
}
|
||||
await index_1.prisma.adminUser.update({
|
||||
where: { id: admin.id },
|
||||
data: { lastLogin: new Date() },
|
||||
});
|
||||
const jwtSecret = process.env['JWT_SECRET'];
|
||||
const jwtExpiresIn = process.env['JWT_EXPIRES_IN'] || '24h';
|
||||
if (!jwtSecret) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Server configuration error',
|
||||
});
|
||||
}
|
||||
const token = jsonwebtoken_1.default.sign({
|
||||
userId: admin.id,
|
||||
username: admin.username
|
||||
}, jwtSecret, {
|
||||
expiresIn: jwtExpiresIn
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Login successful',
|
||||
token,
|
||||
user: {
|
||||
id: admin.id,
|
||||
username: admin.username,
|
||||
},
|
||||
});
|
||||
}));
|
||||
router.get('/verify', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Token is valid',
|
||||
user: req.user,
|
||||
});
|
||||
}));
|
||||
router.post('/logout', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Logged out successfully',
|
||||
});
|
||||
}));
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=auth.js.map
|
||||
1
backend/dist/routes/auth.js.map
vendored
1
backend/dist/routes/auth.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":";;;;;AAAA,qCAAoD;AACpD,wDAA8B;AAC9B,gEAA+B;AAC/B,oCAAkC;AAClC,6DAA0D;AAC1D,6CAA6E;AAC7E,8CAAsB;AAEtB,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAGxB,MAAM,WAAW,GAAG,aAAG,CAAC,MAAM,CAAC;IAC7B,QAAQ,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,QAAQ,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAGH,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAA,2BAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IACvE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAExD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;SAClC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAGrC,MAAM,KAAK,GAAG,MAAM,cAAM,CAAC,SAAS,CAAC,UAAU,CAAC;QAC9C,KAAK,EAAE,EAAE,QAAQ,EAAE;KACpB,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;IACL,CAAC;IAGD,MAAM,eAAe,GAAG,MAAM,kBAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAE3E,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,qBAAqB;SAC/B,CAAC,CAAC;IACL,CAAC;IAGD,MAAM,cAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAC5B,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE;QACvB,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE;KAChC,CAAC,CAAC;IAGH,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC;IAE5D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAG,sBAAG,CAAC,IAAI,CACpB;QACE,MAAM,EAAE,KAAK,CAAC,EAAE;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,EACD,SAAS,EACT;QACE,SAAS,EAAE,YAAY;KACL,CACrB,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,kBAAkB;QAC3B,KAAK;QACL,IAAI,EAAE;YACJ,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;SACzB;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAGJ,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,wBAAiB,EAAE,IAAA,2BAAY,EAAC,KAAK,EAAE,GAAyB,EAAE,GAAa,EAAE,EAAE;IACvG,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,gBAAgB;QACzB,IAAI,EAAE,GAAG,CAAC,IAAI;KACf,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAGJ,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,wBAAiB,EAAE,IAAA,2BAAY,EAAC,KAAK,EAAE,GAAyB,EAAE,GAAa,EAAE,EAAE;IACxG,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,yBAAyB;KACnC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAEJ,kBAAe,MAAM,CAAC"}
|
||||
190
backend/dist/routes/docket.js
vendored
190
backend/dist/routes/docket.js
vendored
|
|
@ -1,190 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const index_1 = require("../index");
|
||||
const errorHandler_1 = require("../middleware/errorHandler");
|
||||
const auth_1 = require("../middleware/auth");
|
||||
const joi_1 = __importDefault(require("joi"));
|
||||
const router = (0, express_1.Router)();
|
||||
function normalizeDate(dateInput) {
|
||||
if (typeof dateInput === 'string') {
|
||||
const [year, month, day] = dateInput.split('-');
|
||||
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
||||
}
|
||||
if (dateInput instanceof Date) {
|
||||
const year = dateInput.getFullYear();
|
||||
const month = dateInput.getMonth();
|
||||
const day = dateInput.getDate();
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
return dateInput;
|
||||
}
|
||||
const createDocketEntrySchema = joi_1.default.object({
|
||||
date: joi_1.default.date().required(),
|
||||
title: joi_1.default.string().required().max(500),
|
||||
summary: joi_1.default.string().required(),
|
||||
notes: joi_1.default.string().optional().allow(''),
|
||||
});
|
||||
const updateDocketEntrySchema = joi_1.default.object({
|
||||
date: joi_1.default.date().optional(),
|
||||
title: joi_1.default.string().optional().max(500),
|
||||
summary: joi_1.default.string().optional(),
|
||||
notes: joi_1.default.string().optional().allow(''),
|
||||
});
|
||||
router.get('/', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const entries = await index_1.prisma.docketEntry.findMany({
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
date: 'asc',
|
||||
},
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
entries,
|
||||
});
|
||||
}));
|
||||
router.get('/:id', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const entryId = parseInt(req.params.id);
|
||||
if (isNaN(entryId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid entry ID',
|
||||
});
|
||||
}
|
||||
const entry = await index_1.prisma.docketEntry.findUnique({
|
||||
where: { id: entryId },
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!entry) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
entry,
|
||||
});
|
||||
}));
|
||||
router.post('/', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const { error, value } = createDocketEntrySchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
const normalizedData = { ...value };
|
||||
if (value.date) {
|
||||
normalizedData.date = normalizeDate(value.date);
|
||||
}
|
||||
const entry = await index_1.prisma.docketEntry.create({
|
||||
data: normalizedData,
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Docket entry created successfully',
|
||||
entry,
|
||||
});
|
||||
}));
|
||||
router.put('/:id', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const entryId = parseInt(req.params.id);
|
||||
if (isNaN(entryId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid entry ID',
|
||||
});
|
||||
}
|
||||
const { error, value } = updateDocketEntrySchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
const normalizedData = { ...value };
|
||||
if (value.date) {
|
||||
normalizedData.date = normalizeDate(value.date);
|
||||
}
|
||||
try {
|
||||
const entry = await index_1.prisma.docketEntry.update({
|
||||
where: { id: entryId },
|
||||
data: normalizedData,
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Docket entry updated successfully',
|
||||
entry,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
router.delete('/:id', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const entryId = parseInt(req.params.id);
|
||||
if (isNaN(entryId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid entry ID',
|
||||
});
|
||||
}
|
||||
try {
|
||||
await index_1.prisma.document.deleteMany({
|
||||
where: { docketEntryId: entryId },
|
||||
});
|
||||
await index_1.prisma.docketEntry.delete({
|
||||
where: { id: entryId },
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Docket entry deleted successfully',
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=docket.js.map
|
||||
1
backend/dist/routes/docket.js.map
vendored
1
backend/dist/routes/docket.js.map
vendored
File diff suppressed because one or more lines are too long
231
backend/dist/routes/documents.js
vendored
231
backend/dist/routes/documents.js
vendored
|
|
@ -1,231 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const index_1 = require("../index");
|
||||
const errorHandler_1 = require("../middleware/errorHandler");
|
||||
const auth_1 = require("../middleware/auth");
|
||||
const multer_1 = __importDefault(require("multer"));
|
||||
const uuid_1 = require("uuid");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const joi_1 = __importDefault(require("joi"));
|
||||
const router = (0, express_1.Router)();
|
||||
const storage = multer_1.default.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const uploadDir = process.env['UPLOAD_DIR'] || './uploads';
|
||||
if (!fs_1.default.existsSync(uploadDir)) {
|
||||
fs_1.default.mkdirSync(uploadDir, { recursive: true });
|
||||
}
|
||||
cb(null, uploadDir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const uuid = (0, uuid_1.v4)();
|
||||
const extension = path_1.default.extname(file.originalname);
|
||||
cb(null, `${uuid}${extension}`);
|
||||
},
|
||||
});
|
||||
const upload = (0, multer_1.default)({
|
||||
storage,
|
||||
limits: {
|
||||
fileSize: parseInt(process.env['MAX_FILE_SIZE'] || '262144000'),
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowedMimeTypes = [
|
||||
'application/pdf',
|
||||
'application/x-pdf',
|
||||
'application/acrobat',
|
||||
'applications/vnd.pdf',
|
||||
'text/pdf',
|
||||
'text/x-pdf'
|
||||
];
|
||||
const isValidPDF = allowedMimeTypes.includes(file.mimetype) ||
|
||||
file.originalname.toLowerCase().endsWith('.pdf');
|
||||
console.log('Upload attempt:', {
|
||||
mimetype: file.mimetype,
|
||||
filename: file.originalname,
|
||||
isValid: isValidPDF
|
||||
});
|
||||
if (isValidPDF) {
|
||||
cb(null, true);
|
||||
}
|
||||
else {
|
||||
cb(new Error(`Only PDF files are allowed. Received: ${file.mimetype} for file: ${file.originalname}`));
|
||||
}
|
||||
},
|
||||
});
|
||||
const createDocumentSchema = joi_1.default.object({
|
||||
docketEntryId: joi_1.default.number().integer().required(),
|
||||
title: joi_1.default.string().required().max(500),
|
||||
summary: joi_1.default.string().optional().allow(''),
|
||||
notes: joi_1.default.string().optional().allow(''),
|
||||
displayOrder: joi_1.default.number().integer().min(0).optional(),
|
||||
});
|
||||
const updateDocumentSchema = joi_1.default.object({
|
||||
title: joi_1.default.string().optional().max(500),
|
||||
summary: joi_1.default.string().optional().allow(''),
|
||||
notes: joi_1.default.string().optional().allow(''),
|
||||
displayOrder: joi_1.default.number().integer().min(0).optional(),
|
||||
});
|
||||
router.get('/:id/download', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const documentId = parseInt(req.params['id'] || '');
|
||||
if (isNaN(documentId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid document ID',
|
||||
});
|
||||
}
|
||||
const document = await index_1.prisma.document.findUnique({
|
||||
where: { id: documentId },
|
||||
});
|
||||
if (!document) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
const filePath = document.filePath;
|
||||
if (!fs_1.default.existsSync(filePath)) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'File not found on server',
|
||||
});
|
||||
}
|
||||
res.setHeader('Content-Type', document.mimeType);
|
||||
res.setHeader('Content-Disposition', `inline; filename="${document.originalFilename}"`);
|
||||
const fileStream = fs_1.default.createReadStream(filePath);
|
||||
fileStream.pipe(res);
|
||||
}));
|
||||
router.post('/upload', auth_1.authenticateToken, upload.single('file'), (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'No file uploaded',
|
||||
});
|
||||
}
|
||||
const { error, value } = createDocumentSchema.validate(req.body);
|
||||
if (error) {
|
||||
fs_1.default.unlinkSync(req.file.path);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
const { docketEntryId, title, summary, notes, displayOrder } = value;
|
||||
const docketEntry = await index_1.prisma.docketEntry.findUnique({
|
||||
where: { id: docketEntryId },
|
||||
});
|
||||
if (!docketEntry) {
|
||||
fs_1.default.unlinkSync(req.file.path);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
let finalDisplayOrder = displayOrder;
|
||||
if (finalDisplayOrder === undefined) {
|
||||
const lastDocument = await index_1.prisma.document.findFirst({
|
||||
where: { docketEntryId },
|
||||
orderBy: { displayOrder: 'desc' },
|
||||
});
|
||||
finalDisplayOrder = lastDocument ? lastDocument.displayOrder + 1 : 0;
|
||||
}
|
||||
const document = await index_1.prisma.document.create({
|
||||
data: {
|
||||
docketEntryId,
|
||||
title,
|
||||
originalFilename: req.file.originalname,
|
||||
storedFilename: req.file.filename,
|
||||
filePath: req.file.path,
|
||||
fileSize: req.file.size,
|
||||
mimeType: req.file.mimetype,
|
||||
summary: summary || '',
|
||||
notes: notes || '',
|
||||
displayOrder: finalDisplayOrder,
|
||||
},
|
||||
});
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Document uploaded successfully',
|
||||
document,
|
||||
});
|
||||
}));
|
||||
router.put('/:id', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const documentId = parseInt(req.params['id'] || '');
|
||||
if (isNaN(documentId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid document ID',
|
||||
});
|
||||
}
|
||||
const { error, value } = updateDocumentSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const document = await index_1.prisma.document.update({
|
||||
where: { id: documentId },
|
||||
data: value,
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Document updated successfully',
|
||||
document,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
router.delete('/:id', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const documentId = parseInt(req.params['id'] || '');
|
||||
if (isNaN(documentId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid document ID',
|
||||
});
|
||||
}
|
||||
try {
|
||||
const document = await index_1.prisma.document.findUnique({
|
||||
where: { id: documentId },
|
||||
});
|
||||
if (!document) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
if (fs_1.default.existsSync(document.filePath)) {
|
||||
fs_1.default.unlinkSync(document.filePath);
|
||||
}
|
||||
await index_1.prisma.document.delete({
|
||||
where: { id: documentId },
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Document deleted successfully',
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=documents.js.map
|
||||
1
backend/dist/routes/documents.js.map
vendored
1
backend/dist/routes/documents.js.map
vendored
File diff suppressed because one or more lines are too long
34
backend/dist/routes/health.js
vendored
34
backend/dist/routes/health.js
vendored
|
|
@ -1,34 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const index_1 = require("../index");
|
||||
const index_2 = require("../index");
|
||||
const router = (0, express_1.Router)();
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
await index_1.prisma.$queryRaw `SELECT 1`;
|
||||
await index_2.redis.ping();
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'API is healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
services: {
|
||||
database: 'connected',
|
||||
redis: 'connected',
|
||||
},
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
res.status(503).json({
|
||||
success: false,
|
||||
message: 'Service unavailable',
|
||||
timestamp: new Date().toISOString(),
|
||||
services: {
|
||||
database: 'disconnected',
|
||||
redis: 'disconnected',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=health.js.map
|
||||
1
backend/dist/routes/health.js.map
vendored
1
backend/dist/routes/health.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,oCAAkC;AAClC,oCAAiC;AAEjC,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IACpD,IAAI,CAAC;QAEH,MAAM,cAAM,CAAC,SAAS,CAAA,UAAU,CAAC;QAGjC,MAAM,aAAK,CAAC,IAAI,EAAE,CAAC;QAEnB,GAAG,CAAC,IAAI,CAAC;YACP,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,gBAAgB;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE;gBACR,QAAQ,EAAE,WAAW;gBACrB,KAAK,EAAE,WAAW;aACnB;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,qBAAqB;YAC9B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE;gBACR,QAAQ,EAAE,cAAc;gBACxB,KAAK,EAAE,cAAc;aACtB;SACF,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
|
||||
104
backend/dist/routes/subscriptions.js
vendored
104
backend/dist/routes/subscriptions.js
vendored
|
|
@ -1,104 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = require("express");
|
||||
const index_1 = require("../index");
|
||||
const errorHandler_1 = require("../middleware/errorHandler");
|
||||
const auth_1 = require("../middleware/auth");
|
||||
const joi_1 = __importDefault(require("joi"));
|
||||
const uuid_1 = require("uuid");
|
||||
const router = (0, express_1.Router)();
|
||||
const subscribeSchema = joi_1.default.object({
|
||||
email: joi_1.default.string().email().required(),
|
||||
});
|
||||
router.post('/subscribe', (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const { error, value } = subscribeSchema.validate(req.body);
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
const { email } = value;
|
||||
const existing = await index_1.prisma.subscription.findUnique({
|
||||
where: { email },
|
||||
});
|
||||
if (existing) {
|
||||
if (existing.isActive) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Email is already subscribed',
|
||||
});
|
||||
}
|
||||
else {
|
||||
await index_1.prisma.subscription.update({
|
||||
where: { email },
|
||||
data: { isActive: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
await index_1.prisma.subscription.create({
|
||||
data: {
|
||||
email,
|
||||
unsubscribeToken: (0, uuid_1.v4)(),
|
||||
},
|
||||
});
|
||||
}
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Successfully subscribed to notifications',
|
||||
});
|
||||
}));
|
||||
router.get('/', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const subscribers = await index_1.prisma.subscription.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
isActive: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
const stats = {
|
||||
total: subscribers.length,
|
||||
active: subscribers.filter(sub => sub.isActive).length,
|
||||
inactive: subscribers.filter(sub => !sub.isActive).length,
|
||||
};
|
||||
res.json({
|
||||
success: true,
|
||||
subscribers,
|
||||
stats,
|
||||
});
|
||||
}));
|
||||
router.put('/:id/toggle', auth_1.authenticateToken, (0, errorHandler_1.asyncHandler)(async (req, res) => {
|
||||
const subscriberId = parseInt(req.params.id);
|
||||
if (isNaN(subscriberId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid subscriber ID',
|
||||
});
|
||||
}
|
||||
const subscriber = await index_1.prisma.subscription.findUnique({
|
||||
where: { id: subscriberId },
|
||||
});
|
||||
if (!subscriber) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Subscriber not found',
|
||||
});
|
||||
}
|
||||
const updated = await index_1.prisma.subscription.update({
|
||||
where: { id: subscriberId },
|
||||
data: { isActive: !subscriber.isActive },
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Subscriber ${updated.isActive ? 'activated' : 'deactivated'} successfully`,
|
||||
subscriber: updated,
|
||||
});
|
||||
}));
|
||||
exports.default = router;
|
||||
//# sourceMappingURL=subscriptions.js.map
|
||||
1
backend/dist/routes/subscriptions.js.map
vendored
1
backend/dist/routes/subscriptions.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/routes/subscriptions.ts"],"names":[],"mappings":";;;;;AAAA,qCAAoD;AACpD,oCAAkC;AAClC,6DAA0D;AAC1D,6CAA6E;AAC7E,8CAAsB;AACtB,+BAAoC;AAEpC,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAGxB,MAAM,eAAe,GAAG,aAAG,CAAC,MAAM,CAAC;IACjC,KAAK,EAAE,aAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC;AAGH,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,IAAA,2BAAY,EAAC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IAC3E,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE5D,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;SAClC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAGxB,MAAM,QAAQ,GAAG,MAAM,cAAM,CAAC,YAAY,CAAC,UAAU,CAAC;QACpD,KAAK,EAAE,EAAE,KAAK,EAAE;KACjB,CAAC,CAAC;IAEH,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBAC1B,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,6BAA6B;aACvC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YAEN,MAAM,cAAM,CAAC,YAAY,CAAC,MAAM,CAAC;gBAC/B,KAAK,EAAE,EAAE,KAAK,EAAE;gBAChB,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;aACzB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;SAAM,CAAC;QAEN,MAAM,cAAM,CAAC,YAAY,CAAC,MAAM,CAAC;YAC/B,IAAI,EAAE;gBACJ,KAAK;gBACL,gBAAgB,EAAE,IAAA,SAAM,GAAE;aAC3B;SACF,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,0CAA0C;KACpD,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAGJ,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,wBAAiB,EAAE,IAAA,2BAAY,EAAC,KAAK,EAAE,GAAyB,EAAE,GAAa,EAAE,EAAE;IACjG,MAAM,WAAW,GAAG,MAAM,cAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;QACrD,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;QAC9B,MAAM,EAAE;YACN,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,IAAI;SAChB;KACF,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG;QACZ,KAAK,EAAE,WAAW,CAAC,MAAM;QACzB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM;QACtD,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM;KAC1D,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,WAAW;QACX,KAAK;KACN,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAGJ,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,wBAAiB,EAAE,IAAA,2BAAY,EAAC,KAAK,EAAE,GAAyB,EAAE,GAAa,EAAE,EAAE;IAC3G,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAE7C,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,cAAM,CAAC,YAAY,CAAC,UAAU,CAAC;QACtD,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE;KAC5B,CAAC,CAAC;IAEH,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YAC1B,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,sBAAsB;SAChC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,cAAM,CAAC,YAAY,CAAC,MAAM,CAAC;QAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE;QAC3B,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;KACzC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC;QACP,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,cAAc,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,eAAe;QACpF,UAAU,EAAE,OAAO;KACpB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAEJ,kBAAe,MAAM,CAAC"}
|
||||
240
backend/dist/services/emailService.js
vendored
240
backend/dist/services/emailService.js
vendored
|
|
@ -1,240 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.emailService = exports.EmailService = void 0;
|
||||
const logger_1 = require("../utils/logger");
|
||||
const nodemailer_1 = __importDefault(require("nodemailer"));
|
||||
class EmailService {
|
||||
static instance;
|
||||
transporter;
|
||||
constructor() {
|
||||
this.transporter = nodemailer_1.default.createTransport({
|
||||
host: process.env['SMTP_HOST'] || 'smtp.gmail.com',
|
||||
port: parseInt(process.env['SMTP_PORT'] || '587'),
|
||||
secure: process.env['SMTP_SECURE'] === 'true',
|
||||
auth: {
|
||||
user: process.env['GOOGLE_EMAIL'],
|
||||
pass: process.env['GOOGLE_APP_PASSWORD'],
|
||||
},
|
||||
});
|
||||
this.verifyConnection();
|
||||
}
|
||||
static getInstance() {
|
||||
if (!EmailService.instance) {
|
||||
EmailService.instance = new EmailService();
|
||||
}
|
||||
return EmailService.instance;
|
||||
}
|
||||
async verifyConnection() {
|
||||
try {
|
||||
await this.transporter.verify();
|
||||
logger_1.logger.info('SMTP connection verified successfully');
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('SMTP connection failed:', error);
|
||||
}
|
||||
}
|
||||
async sendEmail(options) {
|
||||
try {
|
||||
const mailOptions = {
|
||||
from: {
|
||||
name: 'Court Docket System',
|
||||
address: process.env['GOOGLE_EMAIL'] || 'system@deafgain.org',
|
||||
},
|
||||
to: options.to,
|
||||
subject: options.subject,
|
||||
html: options.html,
|
||||
};
|
||||
if (options.bcc && options.bcc.length > 0) {
|
||||
mailOptions.bcc = options.bcc;
|
||||
}
|
||||
const result = await this.transporter.sendMail(mailOptions);
|
||||
logger_1.logger.info('Email sent successfully:', {
|
||||
to: options.to,
|
||||
bcc: options.bcc ? `${options.bcc.length} recipients` : 'none',
|
||||
subject: options.subject,
|
||||
messageId: result.messageId,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to send email:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async notifySubscribersOfNewEntry(entryTitle, entryDate, entrySummary) {
|
||||
try {
|
||||
const { prisma } = await Promise.resolve().then(() => __importStar(require('../index')));
|
||||
const subscribers = await prisma.subscription.findMany({
|
||||
where: { isActive: true },
|
||||
});
|
||||
if (subscribers.length === 0) {
|
||||
logger_1.logger.info('No active subscribers to notify');
|
||||
return;
|
||||
}
|
||||
const subject = `New Court Filing: ${entryTitle}`;
|
||||
const html = this.generateNotificationEmail(entryTitle, entryDate, entrySummary);
|
||||
const subscriberEmails = subscribers.map((subscriber) => subscriber.email);
|
||||
const success = await this.sendEmail({
|
||||
to: process.env['GOOGLE_EMAIL'] || 'system@deafgain.org',
|
||||
bcc: subscriberEmails,
|
||||
subject,
|
||||
html,
|
||||
});
|
||||
if (success) {
|
||||
logger_1.logger.info(`Email notification sent successfully to ${subscriberEmails.length} subscribers via BCC`);
|
||||
}
|
||||
else {
|
||||
logger_1.logger.error(`Failed to send BCC email notification to ${subscriberEmails.length} subscribers`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
logger_1.logger.error('Failed to notify subscribers:', error);
|
||||
}
|
||||
}
|
||||
generateNotificationEmail(title, date, summary) {
|
||||
const formattedDate = new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>New Court Filing Notification</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
background-color: #1f2937;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.content {
|
||||
background-color: #f9fafb;
|
||||
padding: 30px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
.filing-info {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
margin: 20px 0;
|
||||
border-left: 4px solid #3b82f6;
|
||||
}
|
||||
.date {
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #1f2937;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.summary {
|
||||
color: #4b5563;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Eliza Kragh v. Montana Association of the Deaf</h1>
|
||||
<p>New Court Filing Notification</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>A new filing has been added to the court docket:</p>
|
||||
|
||||
<div class="filing-info">
|
||||
<div class="date">${formattedDate}</div>
|
||||
<div class="title">${title}</div>
|
||||
<div class="summary">${summary}</div>
|
||||
</div>
|
||||
|
||||
<p>You can view the complete docket and any associated documents by visiting the <a href="https://mad-lawsuit.org/" style="color: #3b82f6; text-decoration: none;">website</a>:</p>
|
||||
|
||||
<a href="https://mad-lawsuit.org/" class="button">
|
||||
View Court Docket
|
||||
</a>
|
||||
|
||||
<div class="footer">
|
||||
<p>You are receiving this email because you subscribed to notifications for this case.</p>
|
||||
<p>This is an automated notification from the court docket system.</p>
|
||||
<p>To unsubscribe, please send an email to eliza.kragh@gmail.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
}
|
||||
exports.EmailService = EmailService;
|
||||
exports.emailService = EmailService.getInstance();
|
||||
//# sourceMappingURL=emailService.js.map
|
||||
1
backend/dist/services/emailService.js.map
vendored
1
backend/dist/services/emailService.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"emailService.js","sourceRoot":"","sources":["../../src/services/emailService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAAyC;AACzC,4DAAoC;AASpC,MAAa,YAAY;IACf,MAAM,CAAC,QAAQ,CAAe;IAC9B,WAAW,CAAyB;IAE5C;QAEE,IAAI,CAAC,WAAW,GAAG,oBAAU,CAAC,eAAe,CAAC;YAC5C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,gBAAgB;YAClD,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC;YACjD,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,MAAM;YAC7C,IAAI,EAAE;gBACJ,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;gBACjC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;aACzC;SACF,CAAC,CAAC;QAGH,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEM,MAAM,CAAC,WAAW;QACvB,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;YAC3B,YAAY,CAAC,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,YAAY,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;YAChC,eAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAAqB;QACnC,IAAI,CAAC;YACH,MAAM,WAAW,GAAQ;gBACvB,IAAI,EAAE;oBACJ,IAAI,EAAE,qBAAqB;oBAC3B,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,qBAAqB;iBAC9D;gBACD,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC;YAGF,IAAI,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1C,WAAW,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YAChC,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YAE5D,eAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;gBACtC,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,CAAC,CAAC,MAAM;gBAC9D,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;YAC7C,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,2BAA2B,CAAC,UAAkB,EAAE,SAAiB,EAAE,YAAoB;QAC3F,IAAI,CAAC;YAEH,MAAM,EAAE,MAAM,EAAE,GAAG,wDAAa,UAAU,GAAC,CAAC;YAC5C,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;gBACrD,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;aAC1B,CAAC,CAAC;YAEH,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7B,eAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;gBAC/C,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,qBAAqB,UAAU,EAAE,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,yBAAyB,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;YACjF,MAAM,gBAAgB,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,UAAe,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAGhF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC;gBACnC,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,qBAAqB;gBACxD,GAAG,EAAE,gBAAgB;gBACrB,OAAO;gBACP,IAAI;aACL,CAAC,CAAC;YAEH,IAAI,OAAO,EAAE,CAAC;gBACZ,eAAM,CAAC,IAAI,CAAC,2CAA2C,gBAAgB,CAAC,MAAM,sBAAsB,CAAC,CAAC;YACxG,CAAC;iBAAM,CAAC;gBACN,eAAM,CAAC,KAAK,CAAC,4CAA4C,gBAAgB,CAAC,MAAM,cAAc,CAAC,CAAC;YAClG,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAEO,yBAAyB,CAAC,KAAa,EAAE,IAAY,EAAE,OAAe;QAC5E,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;YAC/D,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,GAAG,EAAE,SAAS;SACf,CAAC,CAAC;QAEH,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAgFqB,aAAa;iCACZ,KAAK;mCACH,OAAO;;;;;;;;;;;;;;;;;KAiBrC,CAAC;IACJ,CAAC;CACF;AApND,oCAoNC;AAEY,QAAA,YAAY,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC"}
|
||||
33
backend/dist/utils/logger.js
vendored
33
backend/dist/utils/logger.js
vendored
|
|
@ -1,33 +0,0 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.logger = void 0;
|
||||
const winston_1 = __importDefault(require("winston"));
|
||||
const logFormat = winston_1.default.format.combine(winston_1.default.format.timestamp(), winston_1.default.format.errors({ stack: true }), winston_1.default.format.json());
|
||||
exports.logger = winston_1.default.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: logFormat,
|
||||
defaultMeta: { service: 'docket-api' },
|
||||
transports: [
|
||||
new winston_1.default.transports.File({
|
||||
filename: 'logs/error.log',
|
||||
level: 'error',
|
||||
maxsize: 5242880,
|
||||
maxFiles: 5,
|
||||
}),
|
||||
new winston_1.default.transports.File({
|
||||
filename: 'logs/combined.log',
|
||||
maxsize: 5242880,
|
||||
maxFiles: 5,
|
||||
}),
|
||||
],
|
||||
});
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
exports.logger.add(new winston_1.default.transports.Console({
|
||||
format: winston_1.default.format.combine(winston_1.default.format.colorize(), winston_1.default.format.simple())
|
||||
}));
|
||||
}
|
||||
exports.default = exports.logger;
|
||||
//# sourceMappingURL=logger.js.map
|
||||
1
backend/dist/utils/logger.js.map
vendored
1
backend/dist/utils/logger.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA8B;AAE9B,MAAM,SAAS,GAAG,iBAAO,CAAC,MAAM,CAAC,OAAO,CACtC,iBAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAC1B,iBAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACtC,iBAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CACtB,CAAC;AAEW,QAAA,MAAM,GAAG,iBAAO,CAAC,YAAY,CAAC;IACzC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,MAAM,EAAE,SAAS;IACjB,WAAW,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE;IACtC,UAAU,EAAE;QACV,IAAI,iBAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,gBAAgB;YAC1B,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,OAAO;YAChB,QAAQ,EAAE,CAAC;SACZ,CAAC;QACF,IAAI,iBAAO,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,QAAQ,EAAE,mBAAmB;YAC7B,OAAO,EAAE,OAAO;YAChB,QAAQ,EAAE,CAAC;SACZ,CAAC;KACH;CACF,CAAC,CAAC;AAGH,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;IAC1C,cAAM,CAAC,GAAG,CAAC,IAAI,iBAAO,CAAC,UAAU,CAAC,OAAO,CAAC;QACxC,MAAM,EAAE,iBAAO,CAAC,MAAM,CAAC,OAAO,CAC5B,iBAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EACzB,iBAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CACxB;KACF,CAAC,CAAC,CAAC;AACN,CAAC;AAED,kBAAe,cAAM,CAAC"}
|
||||
|
|
@ -1,809 +0,0 @@
|
|||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:13:09.977Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:13:09.980Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:13:09.984Z"}
|
||||
{"ip":"::ffff:127.0.0.1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:13:57.178Z","url":"/","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"}
|
||||
{"duration":"2ms","ip":"::ffff:127.0.0.1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:13:57.180Z","url":"/"}
|
||||
{"ip":"::ffff:127.0.0.1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:13:57.204Z","url":"/favicon.ico","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:138.0) Gecko/20100101 Firefox/138.0"}
|
||||
{"duration":"1ms","ip":"::ffff:127.0.0.1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:13:57.205Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:14:07.942Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:14:07.944Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:15:56.009Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:15:56.010Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:15:56.012Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:20:19.862Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:20:19.863Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:20:19.866Z"}
|
||||
{"ip":"::ffff:127.0.0.1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:21:02.794Z","url":"/","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::ffff:127.0.0.1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:21:02.796Z","url":"/"}
|
||||
{"ip":"::ffff:127.0.0.1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:21:02.815Z","url":"/favicon.ico","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"1ms","ip":"::ffff:127.0.0.1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:21:02.816Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:21:14.796Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:21:14.799Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:21:25.211Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:21:25.212Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:21:25.214Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:21:37.049Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:21:37.066Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:24:18.294Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:24:18.295Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:28:23.634Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:28:23.636Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:28:23.637Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:30:45.172Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:30:45.174Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:30:45.175Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:31:04.950Z","url":"/api/docket","userAgent":"curl/8.7.1"}
|
||||
{"duration":"0ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:31:04.950Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:31:07.099Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"19ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:31:07.119Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:31:14.621Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:31:14.625Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T21:31:23.756Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"249ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:31:24.004Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T21:31:35.716Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"0ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:31:35.716Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T21:31:54.377Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T21:31:54.799Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T21:31:54.801Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T21:31:54.802Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T21:32:04.251Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"21ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:32:04.272Z","url":"/subscribe"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:36:37.203Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:36:37.209Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:36:44.997Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":404,"timestamp":"2025-06-24T21:36:44.997Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T21:36:52.430Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":400,"timestamp":"2025-06-24T21:36:52.434Z","url":"/subscribe"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:43:55.707Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T21:43:55.725Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T21:44:26.328Z","url":"/api/auth/login","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"266ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T21:44:26.593Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:44:26.803Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T21:44:26.806Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:44:26.809Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T21:44:26.812Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T21:45:50.519Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T21:45:50.523Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:41:20.733Z","url":"/api/auth/login","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"268ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:41:21.001Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:41:21.120Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T22:41:21.127Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:41:21.130Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:41:21.132Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:43:07.980Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
{"error":"Only PDF files are allowed","ip":"::1","level":"error","message":"Error occurred:","method":"POST","service":"docket-api","stack":"Error: Only PDF files are allowed\n at fileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/src/routes/documents.ts:39:10)\n at wrappedFileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/index.js:44:7)\n at Multipart.<anonymous> (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/lib/make-middleware.js:109:7)\n at Multipart.emit (node:events:507:28)\n at HeaderParser.cb (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:358:14)\n at HeaderParser.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:162:20)\n at SBMH.ssCb [as _cb] (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:394:37)\n at feed (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:219:14)\n at SBMH.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:104:16)\n at Multipart._write (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:567:19)","timestamp":"2025-06-24T22:43:07.987Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":500,"timestamp":"2025-06-24T22:43:07.987Z","url":"/api/documents/upload"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T22:44:10.109Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T22:44:10.520Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T22:44:10.522Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T22:44:10.524Z"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T22:44:23.926Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T22:44:24.304Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T22:44:24.306Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T22:44:24.308Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:44:33.165Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"250ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:44:33.415Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:44:33.425Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"html":"\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"...","level":"info","message":"Email would be sent:","service":"docket-api","subject":"New Court Filing: Discovery Motion Filed","timestamp":"2025-06-24T22:44:33.447Z","to":"test@example.com"}
|
||||
{"level":"info","message":"Email notifications sent: 1 successful, 0 failed","service":"docket-api","timestamp":"2025-06-24T22:44:33.548Z"}
|
||||
{"duration":"123ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":201,"timestamp":"2025-06-24T22:44:33.548Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:45:35.068Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:45:35.075Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:45:38.010Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T22:45:38.014Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:47:42.071Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:42.076Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:47:42.084Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:42.087Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:42.099Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:42.105Z","url":"/subscribe"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:42.112Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":400,"timestamp":"2025-06-24T22:47:42.114Z","url":"/subscribe"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:42.122Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"238ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:42.360Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:47:42.374Z","url":"/api/auth/verify","userAgent":"curl/8.7.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:42.376Z","url":"/verify"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:47:42.385Z","url":"/api/docket-entries/1","userAgent":"curl/8.7.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:42.391Z","url":"/1"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:42.402Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"html":"\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"...","level":"info","message":"Email would be sent:","service":"docket-api","subject":"New Court Filing: Automated Test Entry - 17:47:42","timestamp":"2025-06-24T22:47:42.412Z","to":"test@example.com"}
|
||||
{"html":"\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"...","level":"info","message":"Email would be sent:","service":"docket-api","subject":"New Court Filing: Automated Test Entry - 17:47:42","timestamp":"2025-06-24T22:47:42.412Z","to":"test1750805262@example.com"}
|
||||
{"level":"info","message":"Email notifications sent: 2 successful, 0 failed","service":"docket-api","timestamp":"2025-06-24T22:47:42.513Z"}
|
||||
{"duration":"112ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":201,"timestamp":"2025-06-24T22:47:42.514Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:43.537Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
{"duration":"0ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":401,"timestamp":"2025-06-24T22:47:43.537Z","url":"/upload"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:43.550Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
{"error":"Only PDF files are allowed","ip":"::1","level":"error","message":"Error occurred:","method":"POST","service":"docket-api","stack":"Error: Only PDF files are allowed\n at fileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/src/routes/documents.ts:39:10)\n at wrappedFileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/index.js:44:7)\n at Multipart.<anonymous> (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/lib/make-middleware.js:109:7)\n at Multipart.emit (node:events:507:28)\n at HeaderParser.cb (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:358:14)\n at HeaderParser.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:162:20)\n at SBMH.ssCb [as _cb] (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:394:37)\n at feed (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:219:14)\n at SBMH.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:104:16)\n at Multipart._write (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:567:19)","timestamp":"2025-06-24T22:47:43.556Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":500,"timestamp":"2025-06-24T22:47:43.556Z","url":"/api/documents/upload"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:47:43.570Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"231ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":401,"timestamp":"2025-06-24T22:47:43.801Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:47:43.809Z","url":"/api/auth/verify","userAgent":"curl/8.7.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":403,"timestamp":"2025-06-24T22:47:43.810Z","url":"/verify"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:47:43.817Z","url":"/api/health","userAgent":"curl/8.7.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:47:43.822Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-24T22:49:20.164Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-24T22:49:32.273Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-24T22:49:32.275Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-24T22:49:32.278Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-24T22:49:32.777Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:49:42.212Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"248ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:49:42.460Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:49:42.470Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"level":"info","message":"Email sent successfully:","messageId":"<ae446ddd-3cdd-afc2-3b7e-5cd4819c08ae@deafgain.org>","service":"docket-api","subject":"New Court Filing: Real Email Test - Settlement Conference Scheduled","timestamp":"2025-06-24T22:49:43.978Z","to":"test1750805262@example.com"}
|
||||
{"level":"info","message":"Email sent successfully:","messageId":"<e570d07d-b26e-2726-8924-e383604cf0b4@deafgain.org>","service":"docket-api","subject":"New Court Filing: Real Email Test - Settlement Conference Scheduled","timestamp":"2025-06-24T22:49:44.022Z","to":"test@example.com"}
|
||||
{"level":"info","message":"Email notifications sent: 2 successful, 0 failed","service":"docket-api","timestamp":"2025-06-24T22:49:44.024Z"}
|
||||
{"duration":"1555ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":201,"timestamp":"2025-06-24T22:49:44.025Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:51:06.241Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:51:06.247Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:51:50.975Z","url":"/api/subscriptions/subscribe","userAgent":"curl/8.7.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:51:50.982Z","url":"/subscribe"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:52:01.178Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"229ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:52:01.407Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-24T22:52:01.415Z","url":"/api/docket-entries","userAgent":"curl/8.7.1"}
|
||||
{"level":"info","message":"Email sent successfully:","messageId":"<2d07ac41-147a-1249-5fde-395dd03b18f5@deafgain.org>","service":"docket-api","subject":"New Court Filing: Email Test for Chris - Motion for Summary Judgment Filed","timestamp":"2025-06-24T22:52:02.630Z","to":"test@example.com"}
|
||||
{"level":"info","message":"Email sent successfully:","messageId":"<87a974f7-0735-ef00-8f83-bae865916422@deafgain.org>","service":"docket-api","subject":"New Court Filing: Email Test for Chris - Motion for Summary Judgment Filed","timestamp":"2025-06-24T22:52:02.631Z","to":"test1750805262@example.com"}
|
||||
{"level":"info","message":"Email sent successfully:","messageId":"<3e6a094c-9cb0-0e95-b5cd-9de97df11e33@deafgain.org>","service":"docket-api","subject":"New Court Filing: Email Test for Chris - Motion for Summary Judgment Filed","timestamp":"2025-06-24T22:52:04.474Z","to":"chris@sigd.net"}
|
||||
{"level":"info","message":"Email notifications sent: 3 successful, 0 failed","service":"docket-api","timestamp":"2025-06-24T22:52:04.475Z"}
|
||||
{"duration":"3061ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":201,"timestamp":"2025-06-24T22:52:04.476Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T22:58:48.528Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"19ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-24T22:58:48.547Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-24T23:01:13.428Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-24T23:01:13.432Z","url":"/"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T13:38:31.483Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T13:38:31.485Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T13:38:31.489Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T13:38:31.965Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T13:38:50.881Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"21ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T13:38:50.902Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:00:26.370Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"12ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:00:26.382Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:01:04.654Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:01:04.658Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:06:46.367Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"17ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:06:46.384Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T14:06:52.974Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T14:06:53.436Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T14:06:53.437Z"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T14:06:53.439Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T14:06:53.440Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T14:07:10.448Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T14:07:10.449Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T14:07:10.452Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T14:07:10.927Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:07:14.281Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"19ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:07:14.300Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:09:11.796Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:09:11.801Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:16:36.010Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"17ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:16:36.027Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:19:59.753Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:19:59.756Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:20:26.607Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:20:26.612Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:21:36.853Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"17ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:21:36.870Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:29:05.803Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:29:05.821Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:29:22.453Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:29:22.458Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:32:10.797Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:32:10.801Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T14:38:43.507Z","url":"/api/auth/login","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"266ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T14:38:43.773Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:38:44.159Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:38:44.165Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:38:44.167Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T14:38:44.170Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:39:39.010Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:39:39.014Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:40:55.724Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:40:55.729Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:41:42.699Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:41:42.702Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:42:29.563Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:42:29.567Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:42:42.320Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:42:42.323Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:42:42.326Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:42:42.328Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:42:49.326Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:42:49.330Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:42:49.333Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T14:42:49.336Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:44:45.354Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:44:45.362Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"24ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:44:45.378Z","url":"/"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T14:44:45.380Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:49:49.872Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"16ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:49:49.888Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:50:39.092Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:50:39.096Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:50:39.101Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T14:50:39.103Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T14:56:27.090Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"17ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T14:56:27.107Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:00:01.999Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:00:02.003Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:00:17.453Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:00:17.457Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:00:27.417Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:00:27.420Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:00:27.422Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:00:27.425Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:00:29.988Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:00:29.991Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:00:29.994Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:00:29.997Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T15:00:53.837Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T15:00:53.837Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T15:01:10.606Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T15:01:10.607Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T15:01:10.609Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T15:01:11.462Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:15.919Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"19ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:15.938Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:19.275Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:19.279Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:19.284Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:01:19.288Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:20.890Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:20.893Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:20.897Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:20.900Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:22.797Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:22.800Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:22.805Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:22.807Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:24.249Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:24.253Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:01:24.258Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:01:24.261Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:55:43.733Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:55:43.751Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:55:54.282Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:55:54.285Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:55:54.289Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:55:54.291Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:55:56.034Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:55:56.037Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:55:56.040Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:55:56.043Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"PUT","service":"docket-api","timestamp":"2025-06-25T15:56:15.070Z","url":"/api/docket-entries/8","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"14ms","ip":"::1","level":"info","message":"Request completed","method":"PUT","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:56:15.084Z","url":"/8"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:56:15.089Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:56:15.091Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:56:32.552Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:56:32.554Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"PUT","service":"docket-api","timestamp":"2025-06-25T15:57:01.882Z","url":"/api/docket-entries/8","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"15ms","ip":"::1","level":"info","message":"Request completed","method":"PUT","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:57:01.897Z","url":"/8"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:57:01.906Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:57:01.909Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:57:06.435Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:57:06.438Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"PUT","service":"docket-api","timestamp":"2025-06-25T15:57:14.812Z","url":"/api/docket-entries/8","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"10ms","ip":"::1","level":"info","message":"Request completed","method":"PUT","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:57:14.822Z","url":"/8"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:57:14.828Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T15:57:14.831Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:57:18.727Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:57:18.731Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:57:26.740Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:57:26.743Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T15:57:26.747Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T15:57:26.749Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T15:58:15.130Z","url":"/api/documents/upload","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T15:58:52.409Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T15:58:52.410Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T15:58:52.413Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T15:58:52.891Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T15:59:55.718Z","url":"/api/documents/upload","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T16:00:54.943Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T16:00:54.945Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T16:00:54.947Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T16:00:55.507Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T16:01:00.397Z","url":"/api/documents/upload","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"42ms","ip":"::1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":201,"timestamp":"2025-06-25T16:01:00.439Z","url":"/upload"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:01:07.043Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:01:07.049Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:01:16.880Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"12ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:01:16.892Z","url":"/1/download"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:01:45.528Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:01:45.532Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T16:10:56.757Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T16:10:56.758Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T16:10:56.760Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T16:10:57.292Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:11:12.710Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:11:12.713Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T16:12:30.150Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T16:12:30.151Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T16:12:30.153Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T16:12:30.611Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:12:44.316Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"}
|
||||
{"duration":"19ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:12:44.335Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:12:50.408Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:12:50.412Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:12:54.677Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"10ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:12:54.687Z","url":"/1/download"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:13:14.780Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:13:14.785Z","url":"/1/download"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:13:34.059Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:13:34.065Z","url":"/1/download"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:14:03.596Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:14:03.600Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:14:03.604Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:14:03.607Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"PUT","service":"docket-api","timestamp":"2025-06-25T16:14:19.020Z","url":"/api/docket-entries/8","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"PUT","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:14:19.038Z","url":"/8"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:14:19.047Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:14:19.050Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:14:22.721Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:14:22.724Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"PUT","service":"docket-api","timestamp":"2025-06-25T16:14:38.542Z","url":"/api/docket-entries/8","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"12ms","ip":"::1","level":"info","message":"Request completed","method":"PUT","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:14:38.554Z","url":"/8"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:14:38.562Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:14:38.565Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:14:41.134Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:14:41.137Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:18:12.936Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"23ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:18:12.959Z","url":"/1/download"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:18:25.293Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:18:25.296Z","url":"/1/download"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:18:41.211Z","url":"/api/documents/1/download","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:18:41.216Z","url":"/1/download"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:25:03.885Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:25:03.903Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:25:07.631Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:25:07.635Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:25:35.042Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:25:35.046Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"PUT","service":"docket-api","timestamp":"2025-06-25T16:27:04.052Z","url":"/api/docket-entries/8","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"12ms","ip":"::1","level":"info","message":"Request completed","method":"PUT","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:27:04.064Z","url":"/8"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:27:04.071Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:27:04.074Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:27:07.884Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:27:07.887Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:30:38.837Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"17ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:30:38.854Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:32:16.783Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:32:16.788Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:34:58.611Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:34:58.614Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:35:48.523Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:35:48.524Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"22ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:35:48.545Z","url":"/"}
|
||||
{"duration":"21ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:35:48.545Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:36:40.857Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:36:40.861Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:36:40.864Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:36:40.866Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:37:58.921Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:37:58.924Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:37:58.928Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:37:58.931Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:38:04.598Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:38:04.602Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:38:04.605Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:38:04.607Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:38:06.105Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:38:06.108Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:38:06.113Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:38:06.115Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:39:38.668Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:39:38.675Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:39:38.677Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:39:38.680Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:39:40.218Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:39:40.223Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:39:41.858Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:39:41.861Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:39:41.868Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:39:41.870Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:08.199Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:08.204Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:08.207Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:40:08.210Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:38.268Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:38.272Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:39.922Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:39.923Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:39.926Z","url":"/"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:40:39.927Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:44.249Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:44.252Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:44.255Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:44.257Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:48.683Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:48.693Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"29ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:48.713Z","url":"/"}
|
||||
{"duration":"20ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:40:48.713Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:50.184Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:50.188Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:40:50.191Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:40:50.194Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:41:00.830Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:41:00.833Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:44:17.842Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:44:17.846Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:44:19.428Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:44:19.432Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:44:19.435Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:44:19.437Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:44:22.130Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:44:22.134Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:44:22.138Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:44:22.141Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:45:18.485Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:45:18.489Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:45:18.497Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:45:18.500Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:45:21.632Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:45:21.635Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:45:21.639Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:45:21.641Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:51:08.564Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"16ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:51:08.580Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:51:30.090Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:51:30.092Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T16:51:31.610Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T16:51:31.612Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T16:51:31.614Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T16:51:32.064Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:51:40.787Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"19ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:51:40.806Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:52:11.625Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:52:11.630Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:53:15.847Z"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:53:15.849Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T16:53:24.138Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T16:53:24.139Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T16:53:24.141Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T16:53:24.600Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:53:27.326Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:53:27.345Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:53:36.575Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T16:53:36.579Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:56:11.828Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:56:11.832Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:56:27.948Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:56:27.952Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T16:57:40.062Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T16:57:40.066Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T16:58:15.851Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T16:58:16.348Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T16:58:16.349Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T16:58:16.352Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T16:58:16.926Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T17:00:36.490Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T17:00:36.491Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T17:00:36.493Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T17:00:38.052Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:01:08.516Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"18ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T17:01:08.534Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:01:59.122Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:01:59.126Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:02:23.225Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:02:23.230Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:02:23.236Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T17:02:23.239Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:02:26.892Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:02:26.896Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:02:26.902Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T17:02:26.905Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:09:39.693Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"14ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:09:39.707Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:11:13.511Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:11:13.516Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:11:25.325Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:11:25.329Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:11:25.332Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:11:25.335Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:11:28.796Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:11:28.801Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:11:28.804Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:11:28.808Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:15:43.200Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"17ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:15:43.217Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:16:08.337Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:16:08.341Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T17:17:58.237Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T17:17:58.639Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T17:17:58.640Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T17:17:58.643Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T17:17:59.241Z"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T17:18:52.571Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T17:18:52.961Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T17:18:52.963Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T17:18:52.965Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T17:18:53.437Z"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T17:19:09.018Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T17:19:09.401Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T17:19:09.402Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T17:19:09.405Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T17:19:09.897Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:20:39.310Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"22ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:20:39.331Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:20:41.966Z","url":"/api/subscriptions","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"8ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T17:20:41.974Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:20:41.977Z","url":"/api/subscriptions","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T17:20:41.981Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:21:15.553Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T17:21:15.553Z","url":"/api/docket-entries","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":304,"timestamp":"2025-06-25T17:21:15.557Z","url":"/"}
|
||||
{"duration":"16ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T17:21:15.569Z","url":"/"}
|
||||
{"level":"info","message":"SIGINT received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T17:31:17.501Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T17:45:56.359Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T17:45:56.360Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T17:45:56.366Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T17:47:42.726Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T17:47:42.727Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T17:47:42.734Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T18:04:15.731Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T18:04:15.734Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T18:04:15.751Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T18:04:16.426Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:04:18.874Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"121ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:04:18.995Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:04:49.082Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:04:49.089Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:05:01.969Z","url":"/api/health","userAgent":"curl/8.7.1"}
|
||||
{"duration":"10ms","ip":"::ffff:172.18.0.1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:05:01.978Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:05:19.158Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:05:19.163Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:05:49.240Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:05:49.245Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:06:19.334Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:06:19.341Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:06:49.431Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"8ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:06:49.439Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:07:19.528Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:07:19.535Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:07:49.628Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"8ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:07:49.637Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:08:19.720Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:08:19.726Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:08:49.821Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:08:49.825Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:09:19.913Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"41ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:09:19.954Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:09:50.020Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:09:50.024Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:10:20.087Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:10:20.092Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:10:50.143Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:10:50.148Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:11:20.225Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:11:20.231Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:11:50.322Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:11:50.328Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:12:20.401Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:12:20.408Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:12:50.466Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:12:50.471Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:13:20.568Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:13:20.574Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:13:50.669Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:13:50.675Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:14:20.753Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"46ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:14:20.799Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:14:50.887Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:14:50.891Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:15:20.997Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:15:21.001Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:15:51.087Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:15:51.089Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:16:21.182Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:16:21.189Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:16:51.279Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:16:51.282Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:16:53.682Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"3943ms","ip":"::ffff:172.18.0.1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:16:57.626Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:17:21.368Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:17:21.372Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:17:51.429Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:17:51.431Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:18:21.494Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:18:21.496Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:18:51.589Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:18:51.591Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:19:21.684Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"39ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:19:21.723Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:19:51.814Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:19:51.817Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:20:21.896Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:20:21.897Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:20:36.608Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"3889ms","ip":"::ffff:172.18.0.1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:20:40.497Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:20:51.979Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:20:51.981Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:21:22.051Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:21:22.053Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:21:52.115Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:21:52.117Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:22:22.202Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:22:22.204Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:22:52.320Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:22:52.322Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:23:22.390Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:23:22.392Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:23:52.474Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:23:52.475Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:24:22.566Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"40ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:24:22.606Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:24:52.696Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:24:52.699Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:25:22.789Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:25:22.792Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:25:52.888Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:25:52.890Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:26:22.971Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:26:22.973Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:26:53.064Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:26:53.066Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:27:17.986Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"duration":"3908ms","ip":"::ffff:172.18.0.1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:27:21.894Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:27:23.157Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:27:23.159Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:27:53.254Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:27:53.257Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:28:23.322Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:28:23.323Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:28:53.429Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:28:53.431Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:29:23.501Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"37ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:29:23.538Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:29:53.622Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:29:53.625Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:30:23.709Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:30:23.711Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:30:53.799Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:30:53.801Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:31:23.884Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:31:23.886Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:31:53.967Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:31:53.969Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:32:24.053Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:32:24.055Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.5","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:32:41.817Z","url":"/api/auth/login"}
|
||||
{"duration":"3933ms","ip":"::ffff:172.18.0.5","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:32:45.750Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:32:54.142Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:32:54.143Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:33:24.203Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:33:24.206Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:33:54.285Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:33:54.287Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:34:24.358Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"36ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:34:24.394Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:34:54.485Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:34:54.488Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:35:24.553Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:35:24.554Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:35:54.623Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:35:54.625Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:36:24.688Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:36:24.690Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:36:54.778Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:36:54.781Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:37:24.870Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:37:24.873Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:37:54.966Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:37:54.968Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:38:25.064Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:38:25.066Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.5","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:38:50.489Z","url":"/api/auth/login","userAgent":"curl/8.14.1"}
|
||||
{"duration":"3949ms","ip":"::ffff:172.18.0.5","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:38:54.438Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:38:55.165Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:38:55.167Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:39:25.255Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"39ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:39:25.294Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:39:55.387Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:39:55.389Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:40:25.477Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:40:25.480Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:40:55.555Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:40:55.557Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:41:25.646Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:41:25.648Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:41:55.720Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:41:55.723Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:42:25.806Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:42:25.807Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:42:55.895Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:42:55.896Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:43:25.990Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:43:25.993Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:43:56.084Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:43:56.086Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:44:26.148Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"34ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:44:26.182Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:44:56.251Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:44:56.254Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:45:26.322Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:45:26.324Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:45:56.418Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:45:56.420Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:46:26.513Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:46:26.514Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.5","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:46:38.766Z","url":"/api/auth/login","userAgent":"node"}
|
||||
{"duration":"3924ms","ip":"::ffff:172.18.0.5","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:46:42.690Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:46:56.586Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:46:56.588Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:47:26.677Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"12ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:47:26.689Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:47:56.755Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:47:56.758Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:48:26.824Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:48:26.826Z","url":"/"}
|
||||
{"ip":"::ffff:172.18.0.1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:48:55.788Z","url":"/api/auth/login","userAgent":"curl/8.7.1"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:48:57.020Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"308ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:48:57.329Z","url":"/"}
|
||||
{"duration":"3958ms","ip":"::ffff:172.18.0.1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:48:59.746Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:49:27.422Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"46ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:49:27.468Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:49:57.539Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:49:57.542Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:50:27.623Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:50:27.625Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:50:57.714Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:50:57.718Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:51:27.808Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:51:27.809Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:51:57.862Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"1ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:51:57.863Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:52:27.952Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:52:27.954Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:52:58.038Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:52:58.040Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:53:28.130Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:53:28.132Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:53:58.224Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:53:58.226Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:54:28.315Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"37ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:54:28.353Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:54:58.434Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"3ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:54:58.437Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:55:28.521Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"2ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:55:28.523Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T18:55:47.633Z"}
|
||||
{"level":"info","message":"Server running on port 3001","service":"docket-api","timestamp":"2025-06-25T18:56:03.229Z"}
|
||||
{"level":"info","message":"Environment: development","service":"docket-api","timestamp":"2025-06-25T18:56:03.232Z"}
|
||||
{"level":"info","message":"Connected to Redis","service":"docket-api","timestamp":"2025-06-25T18:56:03.250Z"}
|
||||
{"level":"info","message":"SMTP connection verified successfully","service":"docket-api","timestamp":"2025-06-25T18:56:03.817Z"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:56:06.438Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"121ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:56:06.559Z","url":"/"}
|
||||
{"ip":"::ffff:172.19.0.5","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:56:25.990Z","url":"/api/health"}
|
||||
{"duration":"6ms","ip":"::ffff:172.19.0.5","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:56:25.995Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:56:36.639Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"8ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:56:36.647Z","url":"/"}
|
||||
{"ip":"::ffff:172.19.0.5","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:56:44.561Z","url":"/api/auth/login"}
|
||||
{"duration":"3900ms","ip":"::ffff:172.19.0.5","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:56:48.461Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:57:06.716Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:57:06.722Z","url":"/"}
|
||||
{"ip":"::ffff:172.19.0.1","level":"info","message":"Incoming request","method":"POST","service":"docket-api","timestamp":"2025-06-25T18:57:22.523Z","url":"/api/auth/login","userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0"}
|
||||
{"duration":"3981ms","ip":"::ffff:172.19.0.1","level":"info","message":"Request completed","method":"POST","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:57:26.505Z","url":"/login"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:57:36.810Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"13ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:57:36.824Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:58:06.907Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:58:06.912Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:58:37.002Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:58:37.005Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:59:07.101Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"4ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:59:07.106Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T18:59:37.200Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"5ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T18:59:37.205Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:00:07.300Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:00:07.306Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:00:37.403Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:00:37.409Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:01:07.498Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"45ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:01:07.542Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:01:37.603Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:01:37.609Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:02:07.688Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"7ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:02:07.696Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:02:37.793Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"8ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:02:37.801Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:03:07.892Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"6ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:03:07.898Z","url":"/"}
|
||||
{"ip":"::1","level":"info","message":"Incoming request","method":"GET","service":"docket-api","timestamp":"2025-06-25T19:03:37.989Z","url":"/api/health","userAgent":"curl/8.12.1"}
|
||||
{"duration":"8ms","ip":"::1","level":"info","message":"Request completed","method":"GET","service":"docket-api","statusCode":200,"timestamp":"2025-06-25T19:03:37.996Z","url":"/"}
|
||||
{"level":"info","message":"SIGTERM received, shutting down gracefully","service":"docket-api","timestamp":"2025-06-25T19:03:54.773Z"}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
{"error":"Only PDF files are allowed","ip":"::1","level":"error","message":"Error occurred:","method":"POST","service":"docket-api","stack":"Error: Only PDF files are allowed\n at fileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/src/routes/documents.ts:39:10)\n at wrappedFileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/index.js:44:7)\n at Multipart.<anonymous> (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/lib/make-middleware.js:109:7)\n at Multipart.emit (node:events:507:28)\n at HeaderParser.cb (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:358:14)\n at HeaderParser.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:162:20)\n at SBMH.ssCb [as _cb] (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:394:37)\n at feed (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:219:14)\n at SBMH.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:104:16)\n at Multipart._write (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:567:19)","timestamp":"2025-06-24T22:43:07.987Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
{"error":"Only PDF files are allowed","ip":"::1","level":"error","message":"Error occurred:","method":"POST","service":"docket-api","stack":"Error: Only PDF files are allowed\n at fileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/src/routes/documents.ts:39:10)\n at wrappedFileFilter (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/index.js:44:7)\n at Multipart.<anonymous> (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/multer@1.4.5-lts.2/node_modules/multer/lib/make-middleware.js:109:7)\n at Multipart.emit (node:events:507:28)\n at HeaderParser.cb (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:358:14)\n at HeaderParser.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:162:20)\n at SBMH.ssCb [as _cb] (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:394:37)\n at feed (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:219:14)\n at SBMH.push (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/streamsearch@1.1.0/node_modules/streamsearch/lib/sbmh.js:104:16)\n at Multipart._write (/Users/chaulmark/Eliza-MAD-docket-website/backend/node_modules/.pnpm/busboy@1.6.0/node_modules/busboy/lib/types/multipart.js:567:19)","timestamp":"2025-06-24T22:47:43.556Z","url":"/api/documents/upload","userAgent":"curl/8.7.1"}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
"name": "eliza-mad-docket-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "Backend API for Eliza Kragh v. MAD court docket website",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"prisma:generate": "prisma generate",
|
||||
"prisma:migrate": "prisma migrate dev",
|
||||
"prisma:deploy": "prisma migrate deploy",
|
||||
"prisma:studio": "prisma studio",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"lint:fix": "eslint src --ext .ts --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/adapter-pg": "^7.1.0",
|
||||
"@prisma/client": "^7.1.0",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"compression": "^1.8.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.2.1",
|
||||
"helmet": "^8.1.0",
|
||||
"joi": "^18.0.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"multer": "^2.0.2",
|
||||
"nodemailer": "^7.0.11",
|
||||
"pg": "^8.16.3",
|
||||
"prisma": "^7.1.0",
|
||||
"redis": "^5.10.0",
|
||||
"uuid": "^13.0.0",
|
||||
"winston": "^3.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^3.0.0",
|
||||
"@types/compression": "^1.8.1",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/node": "^25.0.2",
|
||||
"@types/nodemailer": "^7.0.4",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
||||
"@typescript-eslint/parser": "^8.49.0",
|
||||
"eslint": "^9.39.2",
|
||||
"jest": "^30.2.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
}
|
||||
}
|
||||
6809
backend/pnpm-lock.yaml
generated
6809
backend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +0,0 @@
|
|||
import "dotenv/config";
|
||||
import { defineConfig, env } from "prisma/config";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "prisma/schema.prisma",
|
||||
migrations: {
|
||||
path: "prisma/migrations",
|
||||
},
|
||||
datasource: {
|
||||
url: env("DATABASE_URL"),
|
||||
},
|
||||
});
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "docket_entries" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"date" DATE NOT NULL,
|
||||
"document_type" VARCHAR(100) NOT NULL,
|
||||
"summary" TEXT NOT NULL,
|
||||
"court_notes" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "docket_entries_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "documents" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"docket_entry_id" INTEGER NOT NULL,
|
||||
"original_filename" VARCHAR(255) NOT NULL,
|
||||
"stored_filename" VARCHAR(255) NOT NULL,
|
||||
"file_path" VARCHAR(500) NOT NULL,
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"summary" TEXT,
|
||||
"notes" TEXT,
|
||||
"is_primary" BOOLEAN NOT NULL DEFAULT false,
|
||||
"file_size" INTEGER NOT NULL,
|
||||
"display_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "documents_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "subscriptions" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"email" VARCHAR(255) NOT NULL,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"unsubscribe_token" VARCHAR(255) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "subscriptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "admin_users" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"username" VARCHAR(50) NOT NULL,
|
||||
"password_hash" VARCHAR(255) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"last_login" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "admin_users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_docket_entries_date" ON "docket_entries"("date" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "documents_stored_filename_key" ON "documents"("stored_filename");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_documents_docket_entry" ON "documents"("docket_entry_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_documents_display_order" ON "documents"("docket_entry_id", "display_order");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "subscriptions_email_key" ON "subscriptions"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "subscriptions_unsubscribe_token_key" ON "subscriptions"("unsubscribe_token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "idx_subscriptions_active" ON "subscriptions"("is_active");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "admin_users_username_key" ON "admin_users"("username");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "documents" ADD CONSTRAINT "documents_docket_entry_id_fkey" FOREIGN KEY ("docket_entry_id") REFERENCES "docket_entries"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `court_notes` on the `docket_entries` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `document_type` on the `docket_entries` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `is_primary` on the `documents` table. All the data in the column will be lost.
|
||||
- Added the required column `title` to the `docket_entries` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `mime_type` to the `documents` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "docket_entries" DROP COLUMN "court_notes",
|
||||
DROP COLUMN "document_type",
|
||||
ADD COLUMN "notes" TEXT,
|
||||
ADD COLUMN "title" VARCHAR(500) NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "documents" DROP COLUMN "is_primary",
|
||||
ADD COLUMN "mime_type" VARCHAR(100) NOT NULL,
|
||||
ALTER COLUMN "title" SET DATA TYPE VARCHAR(500);
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
}
|
||||
|
||||
model DocketEntry {
|
||||
id Int @id @default(autoincrement())
|
||||
date DateTime @db.Date
|
||||
title String @db.VarChar(500)
|
||||
summary String @db.Text
|
||||
notes String? @db.Text
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
documents Document[]
|
||||
|
||||
@@map("docket_entries")
|
||||
@@index([date(sort: Desc)], name: "idx_docket_entries_date")
|
||||
}
|
||||
|
||||
model Document {
|
||||
id Int @id @default(autoincrement())
|
||||
docketEntryId Int @map("docket_entry_id")
|
||||
title String @db.VarChar(500)
|
||||
originalFilename String @map("original_filename") @db.VarChar(255)
|
||||
storedFilename String @unique @map("stored_filename") @db.VarChar(255)
|
||||
filePath String @map("file_path") @db.VarChar(500)
|
||||
fileSize Int @map("file_size")
|
||||
mimeType String @map("mime_type") @db.VarChar(100)
|
||||
summary String? @db.Text
|
||||
notes String? @db.Text
|
||||
displayOrder Int @default(0) @map("display_order")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
docketEntry DocketEntry @relation(fields: [docketEntryId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("documents")
|
||||
@@index([docketEntryId], name: "idx_documents_docket_entry")
|
||||
@@index([docketEntryId, displayOrder], name: "idx_documents_display_order")
|
||||
}
|
||||
|
||||
model Subscription {
|
||||
id Int @id @default(autoincrement())
|
||||
email String @unique @db.VarChar(255)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
unsubscribeToken String @unique @map("unsubscribe_token") @db.VarChar(255)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
@@map("subscriptions")
|
||||
@@index([isActive], name: "idx_subscriptions_active")
|
||||
}
|
||||
|
||||
model AdminUser {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique @db.VarChar(50)
|
||||
passwordHash String @map("password_hash") @db.VarChar(255)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
lastLogin DateTime? @map("last_login")
|
||||
|
||||
@@map("admin_users")
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function setupAdmin() {
|
||||
try {
|
||||
console.log('🔧 Setting up admin user for production...');
|
||||
|
||||
// Generate secure credentials
|
||||
const username = 'eliza';
|
||||
const password = crypto.randomBytes(12).toString('base64').replace(/[+/=]/g, '').substring(0, 16);
|
||||
|
||||
console.log('\n📋 Generated Credentials:');
|
||||
console.log('Username:', username);
|
||||
console.log('Password:', password);
|
||||
console.log('\n⚠️ IMPORTANT: Save these credentials securely!');
|
||||
|
||||
// Hash the password
|
||||
const saltRounds = 12;
|
||||
const passwordHash = await bcrypt.hash(password, saltRounds);
|
||||
|
||||
// Check if admin user already exists
|
||||
const existingAdmin = await prisma.adminUser.findUnique({
|
||||
where: { username }
|
||||
});
|
||||
|
||||
if (existingAdmin) {
|
||||
// Update existing admin
|
||||
await prisma.adminUser.update({
|
||||
where: { username },
|
||||
data: {
|
||||
passwordHash
|
||||
}
|
||||
});
|
||||
console.log('\n✅ Admin user updated successfully!');
|
||||
} else {
|
||||
// Create new admin user
|
||||
await prisma.adminUser.create({
|
||||
data: {
|
||||
username,
|
||||
passwordHash
|
||||
}
|
||||
});
|
||||
console.log('\n✅ Admin user created successfully!');
|
||||
}
|
||||
|
||||
console.log('\n🚀 Production setup complete!');
|
||||
console.log('Eliza can now log in with the credentials above.');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error setting up admin user:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the setup
|
||||
setupAdmin();
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const bcrypt = require('bcryptjs');
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function setupAdmin() {
|
||||
try {
|
||||
console.log('🔧 Setting up admin user for production...');
|
||||
|
||||
// Generate secure credentials
|
||||
const username = 'eliza';
|
||||
const password = crypto.randomBytes(12).toString('base64').replace(/[+/=]/g, '').substring(0, 16);
|
||||
|
||||
console.log('\n📋 Generated Credentials:');
|
||||
console.log('Username:', username);
|
||||
console.log('Password:', password);
|
||||
console.log('\n⚠️ IMPORTANT: Save these credentials securely!');
|
||||
|
||||
// Hash the password
|
||||
const saltRounds = 12;
|
||||
const passwordHash = await bcrypt.hash(password, saltRounds);
|
||||
|
||||
// Check if admin user already exists
|
||||
const existingAdmin = await prisma.adminUser.findUnique({
|
||||
where: { username }
|
||||
});
|
||||
|
||||
if (existingAdmin) {
|
||||
// Update existing admin
|
||||
await prisma.adminUser.update({
|
||||
where: { username },
|
||||
data: {
|
||||
passwordHash,
|
||||
updatedAt: new Date()
|
||||
}
|
||||
});
|
||||
console.log('\n✅ Admin user updated successfully!');
|
||||
} else {
|
||||
// Create new admin user
|
||||
await prisma.adminUser.create({
|
||||
data: {
|
||||
username,
|
||||
passwordHash,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}
|
||||
});
|
||||
console.log('\n✅ Admin user created successfully!');
|
||||
}
|
||||
|
||||
console.log('\n🚀 Production setup complete!');
|
||||
console.log('Eliza can now log in with the credentials above.');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error setting up admin user:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the setup
|
||||
setupAdmin();
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import helmet from 'helmet';
|
||||
import compression from 'compression';
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import dotenv from 'dotenv';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import pg from 'pg';
|
||||
import { createClient } from 'redis';
|
||||
import { logger } from './utils/logger';
|
||||
import { errorHandler } from './middleware/errorHandler';
|
||||
import { requestLogger } from './middleware/requestLogger';
|
||||
|
||||
// Import routes
|
||||
import authRoutes from './routes/auth';
|
||||
import docketRoutes from './routes/docket';
|
||||
import documentRoutes from './routes/documents';
|
||||
import subscriptionRoutes from './routes/subscriptions';
|
||||
import healthRoutes from './routes/health';
|
||||
import notificationRoutes from './routes/notifications';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
// Trust proxy when behind reverse proxy (Caddy)
|
||||
app.set('trust proxy', true);
|
||||
|
||||
// Initialize Prisma client with PostgreSQL adapter
|
||||
const connectionString = process.env.DATABASE_URL;
|
||||
const pool = new pg.Pool({ connectionString });
|
||||
const adapter = new PrismaPg(pool);
|
||||
|
||||
export const prisma = new PrismaClient({
|
||||
adapter,
|
||||
log: ['query', 'info', 'warn', 'error'],
|
||||
});
|
||||
|
||||
// Initialize Redis client
|
||||
export const redis = createClient({
|
||||
url: process.env.REDIS_URL || 'redis://localhost:6379',
|
||||
});
|
||||
|
||||
// Connect to Redis
|
||||
redis.connect().catch((error) => {
|
||||
logger.error('Redis connection failed:', error);
|
||||
});
|
||||
|
||||
redis.on('error', (error) => {
|
||||
logger.error('Redis error:', error);
|
||||
});
|
||||
|
||||
redis.on('connect', () => {
|
||||
logger.info('Connected to Redis');
|
||||
});
|
||||
|
||||
// Security middleware
|
||||
app.use(helmet({
|
||||
crossOriginResourcePolicy: { policy: "cross-origin" }
|
||||
}));
|
||||
|
||||
// CORS configuration
|
||||
app.use(cors({
|
||||
origin: process.env.NODE_ENV === 'production'
|
||||
? [
|
||||
process.env.FRONTEND_URL,
|
||||
'https://mad-lawsuit.org',
|
||||
'https://files.mad-lawsuit.org',
|
||||
'http://frontend:806',
|
||||
'http://localhost:806'
|
||||
]
|
||||
: [
|
||||
'http://localhost:3000',
|
||||
'http://localhost:806',
|
||||
'http://frontend:806',
|
||||
'http://frontend:3000',
|
||||
'https://mad-lawsuit.org',
|
||||
'https://files.mad-lawsuit.org'
|
||||
],
|
||||
credentials: true,
|
||||
}));
|
||||
|
||||
// Rate limiting with proper proxy configuration
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // limit each IP to 100 requests per windowMs
|
||||
message: 'Too many requests from this IP, please try again later.',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
// Skip rate limiting validation errors in production behind proxy
|
||||
skip: (req) => {
|
||||
// Skip rate limiting if we can't determine the real IP
|
||||
return false;
|
||||
},
|
||||
keyGenerator: (req) => {
|
||||
// Use X-Forwarded-For header if available, otherwise fall back to req.ip
|
||||
return req.ip || 'unknown';
|
||||
},
|
||||
});
|
||||
|
||||
app.use('/api', limiter);
|
||||
|
||||
// Stricter rate limiting for auth endpoints
|
||||
const authLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // limit each IP to 5 requests per windowMs
|
||||
message: 'Too many authentication attempts, please try again later.',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req) => {
|
||||
return req.ip || 'unknown';
|
||||
},
|
||||
});
|
||||
|
||||
app.use('/api/auth', authLimiter);
|
||||
|
||||
// Body parsing middleware
|
||||
app.use(compression());
|
||||
app.use(express.json({ limit: '10mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
||||
|
||||
// Request logging
|
||||
app.use(requestLogger);
|
||||
|
||||
// Routes
|
||||
app.use('/api/health', healthRoutes);
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/docket-entries', docketRoutes);
|
||||
app.use('/api/documents', documentRoutes);
|
||||
app.use('/api/subscriptions', subscriptionRoutes);
|
||||
app.use('/api/notifications', notificationRoutes);
|
||||
|
||||
// 404 handler - Express 5 compatible
|
||||
app.use((req, res) => {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: 'Route not found',
|
||||
});
|
||||
});
|
||||
|
||||
// Error handling middleware (must be last)
|
||||
app.use(errorHandler);
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGTERM', async () => {
|
||||
logger.info('SIGTERM received, shutting down gracefully');
|
||||
|
||||
// Close database connection
|
||||
await prisma.$disconnect();
|
||||
|
||||
// Close Redis connection
|
||||
await redis.quit();
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
logger.info('SIGINT received, shutting down gracefully');
|
||||
|
||||
// Close database connection
|
||||
await prisma.$disconnect();
|
||||
|
||||
// Close Redis connection
|
||||
await redis.quit();
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Start server
|
||||
app.listen(PORT, () => {
|
||||
logger.info(`Server running on port ${PORT}`);
|
||||
logger.info(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
||||
});
|
||||
|
||||
export default app;
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { prisma } from '../index';
|
||||
|
||||
export interface AuthenticatedRequest extends Request {
|
||||
user?: {
|
||||
userId: number;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const authenticateToken = async (
|
||||
req: AuthenticatedRequest,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Access token required',
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const jwtSecret = process.env['JWT_SECRET'];
|
||||
if (!jwtSecret) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Server configuration error',
|
||||
});
|
||||
}
|
||||
|
||||
const decoded = jwt.verify(token, jwtSecret) as any;
|
||||
|
||||
// Verify user still exists
|
||||
const user = await prisma.adminUser.findUnique({
|
||||
where: { id: decoded.userId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid token - user not found',
|
||||
});
|
||||
}
|
||||
|
||||
req.user = {
|
||||
userId: decoded.userId,
|
||||
username: decoded.username,
|
||||
};
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
message: 'Invalid or expired token',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
export interface AppError extends Error {
|
||||
statusCode?: number;
|
||||
isOperational?: boolean;
|
||||
}
|
||||
|
||||
export const errorHandler = (
|
||||
error: AppError,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): void => {
|
||||
const statusCode = error.statusCode || 500;
|
||||
const message = error.message || 'Internal Server Error';
|
||||
|
||||
// Log error details
|
||||
logger.error('Error occurred:', {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
url: req.url,
|
||||
method: req.method,
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
});
|
||||
|
||||
// Don't leak error details in production
|
||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
message: isDevelopment ? message : 'Something went wrong',
|
||||
...(isDevelopment && { stack: error.stack }),
|
||||
});
|
||||
};
|
||||
|
||||
export const createError = (message: string, statusCode: number = 500): AppError => {
|
||||
const error: AppError = new Error(message);
|
||||
error.statusCode = statusCode;
|
||||
error.isOperational = true;
|
||||
return error;
|
||||
};
|
||||
|
||||
export const asyncHandler = (fn: Function) => {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { Request, Response, NextFunction } from 'express';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
export const requestLogger = (req: Request, res: Response, next: NextFunction): void => {
|
||||
const start = Date.now();
|
||||
|
||||
// Log request
|
||||
logger.info('Incoming request', {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
ip: req.ip,
|
||||
userAgent: req.get('User-Agent'),
|
||||
});
|
||||
|
||||
// Override res.end to log response
|
||||
const originalEnd = res.end;
|
||||
res.end = function(chunk?: any, encoding?: any): Response {
|
||||
const duration = Date.now() - start;
|
||||
|
||||
logger.info('Request completed', {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
statusCode: res.statusCode,
|
||||
duration: `${duration}ms`,
|
||||
ip: req.ip,
|
||||
});
|
||||
|
||||
return originalEnd.call(this, chunk, encoding);
|
||||
} as any;
|
||||
|
||||
next();
|
||||
};
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { prisma } from '../index';
|
||||
import { asyncHandler } from '../middleware/errorHandler';
|
||||
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
||||
import Joi from 'joi';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Validation schemas
|
||||
const loginSchema = Joi.object({
|
||||
username: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
});
|
||||
|
||||
// Login endpoint
|
||||
router.post('/login', asyncHandler(async (req: Request, res: Response) => {
|
||||
const { error, value } = loginSchema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
const { username, password } = value;
|
||||
|
||||
// Find admin user
|
||||
const admin = await prisma.adminUser.findUnique({
|
||||
where: { username },
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid credentials',
|
||||
});
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const isValidPassword = await bcrypt.compare(password, admin.passwordHash);
|
||||
|
||||
if (!isValidPassword) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: 'Invalid credentials',
|
||||
});
|
||||
}
|
||||
|
||||
// Update last login
|
||||
await prisma.adminUser.update({
|
||||
where: { id: admin.id },
|
||||
data: { lastLogin: new Date() },
|
||||
});
|
||||
|
||||
// Generate JWT token
|
||||
const jwtSecret = process.env['JWT_SECRET'];
|
||||
const jwtExpiresIn = process.env['JWT_EXPIRES_IN'] || '24h';
|
||||
|
||||
if (!jwtSecret) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Server configuration error',
|
||||
});
|
||||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: admin.id,
|
||||
username: admin.username
|
||||
},
|
||||
jwtSecret,
|
||||
{
|
||||
expiresIn: jwtExpiresIn
|
||||
} as jwt.SignOptions
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Login successful',
|
||||
token,
|
||||
user: {
|
||||
id: admin.id,
|
||||
username: admin.username,
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
// Verify token endpoint
|
||||
router.get('/verify', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Token is valid',
|
||||
user: req.user,
|
||||
});
|
||||
}));
|
||||
|
||||
// Logout endpoint (client-side token removal, but we can log it)
|
||||
router.post('/logout', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Logged out successfully',
|
||||
});
|
||||
}));
|
||||
|
||||
export default router;
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import { prisma } from '../index';
|
||||
import { asyncHandler } from '../middleware/errorHandler';
|
||||
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
||||
import { emailService } from '../services/emailService';
|
||||
import Joi from 'joi';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Helper function to normalize date input to avoid timezone issues
|
||||
function normalizeDate(dateInput: any): Date {
|
||||
if (typeof dateInput === 'string') {
|
||||
// If it's a string in YYYY-MM-DD format, create date in local time
|
||||
const [year, month, day] = dateInput.split('-');
|
||||
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
||||
}
|
||||
if (dateInput instanceof Date) {
|
||||
// If it's already a Date object, extract date parts and recreate to avoid timezone issues
|
||||
const year = dateInput.getFullYear();
|
||||
const month = dateInput.getMonth();
|
||||
const day = dateInput.getDate();
|
||||
return new Date(year, month, day);
|
||||
}
|
||||
return dateInput;
|
||||
}
|
||||
|
||||
// Validation schemas
|
||||
const createDocketEntrySchema = Joi.object({
|
||||
date: Joi.date().required(),
|
||||
title: Joi.string().required().max(500),
|
||||
summary: Joi.string().required(),
|
||||
notes: Joi.string().optional().allow(''),
|
||||
});
|
||||
|
||||
const updateDocketEntrySchema = Joi.object({
|
||||
date: Joi.date().optional(),
|
||||
title: Joi.string().optional().max(500),
|
||||
summary: Joi.string().optional(),
|
||||
notes: Joi.string().optional().allow(''),
|
||||
});
|
||||
|
||||
// Public endpoint - Get all docket entries with documents
|
||||
router.get('/', asyncHandler(async (req: Request, res: Response) => {
|
||||
const entries = await prisma.docketEntry.findMany({
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
date: 'asc',
|
||||
},
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
entries,
|
||||
});
|
||||
}));
|
||||
|
||||
// Admin endpoint - Get single docket entry
|
||||
router.get('/:id', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const entryId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(entryId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid entry ID',
|
||||
});
|
||||
}
|
||||
|
||||
const entry = await prisma.docketEntry.findUnique({
|
||||
where: { id: entryId },
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!entry) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
entry,
|
||||
});
|
||||
}));
|
||||
|
||||
// Admin endpoint - Create new docket entry
|
||||
router.post('/', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const { error, value } = createDocketEntrySchema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
// Normalize date to avoid timezone issues
|
||||
const normalizedData = { ...value };
|
||||
if (value.date) {
|
||||
normalizedData.date = normalizeDate(value.date);
|
||||
}
|
||||
|
||||
const entry = await prisma.docketEntry.create({
|
||||
data: normalizedData,
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Email notifications are now manual-only via the green mail button
|
||||
// Automatic notifications disabled per user request
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Docket entry created successfully',
|
||||
entry,
|
||||
});
|
||||
}));
|
||||
|
||||
// Admin endpoint - Update docket entry
|
||||
router.put('/:id', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const entryId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(entryId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid entry ID',
|
||||
});
|
||||
}
|
||||
|
||||
const { error, value } = updateDocketEntrySchema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
// Normalize date to avoid timezone issues
|
||||
const normalizedData = { ...value };
|
||||
if (value.date) {
|
||||
normalizedData.date = normalizeDate(value.date);
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await prisma.docketEntry.update({
|
||||
where: { id: entryId },
|
||||
data: normalizedData,
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Docket entry updated successfully',
|
||||
entry,
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
|
||||
// Admin endpoint - Delete docket entry
|
||||
router.delete('/:id', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const entryId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(entryId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid entry ID',
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Delete associated documents first (cascade should handle this, but being explicit)
|
||||
await prisma.document.deleteMany({
|
||||
where: { docketEntryId: entryId },
|
||||
});
|
||||
|
||||
// Delete the docket entry
|
||||
await prisma.docketEntry.delete({
|
||||
where: { id: entryId },
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Docket entry deleted successfully',
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
|
||||
export default router;
|
||||
|
|
@ -1,276 +0,0 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import { prisma } from '../index';
|
||||
import { asyncHandler } from '../middleware/errorHandler';
|
||||
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
||||
import multer from 'multer';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import Joi from 'joi';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Configure multer for file uploads
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const uploadDir = process.env['UPLOAD_DIR'] || './uploads';
|
||||
if (!fs.existsSync(uploadDir)) {
|
||||
fs.mkdirSync(uploadDir, { recursive: true });
|
||||
}
|
||||
cb(null, uploadDir);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
const uuid = uuidv4();
|
||||
const extension = path.extname(file.originalname);
|
||||
cb(null, `${uuid}${extension}`);
|
||||
},
|
||||
});
|
||||
|
||||
const upload = multer({
|
||||
storage,
|
||||
limits: {
|
||||
fileSize: parseInt(process.env['MAX_FILE_SIZE'] || '262144000'), // 250MB default
|
||||
},
|
||||
fileFilter: (req, file, cb) => {
|
||||
// Allow PDF files with multiple possible MIME types
|
||||
const allowedMimeTypes = [
|
||||
'application/pdf',
|
||||
'application/x-pdf',
|
||||
'application/acrobat',
|
||||
'applications/vnd.pdf',
|
||||
'text/pdf',
|
||||
'text/x-pdf'
|
||||
];
|
||||
|
||||
// Check MIME type or file extension
|
||||
const isValidPDF = allowedMimeTypes.includes(file.mimetype) ||
|
||||
file.originalname.toLowerCase().endsWith('.pdf');
|
||||
|
||||
// Log for debugging
|
||||
console.log('Upload attempt:', {
|
||||
mimetype: file.mimetype,
|
||||
filename: file.originalname,
|
||||
isValid: isValidPDF
|
||||
});
|
||||
|
||||
if (isValidPDF) {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(new Error(`Only PDF files are allowed. Received: ${file.mimetype} for file: ${file.originalname}`));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Validation schemas
|
||||
const createDocumentSchema = Joi.object({
|
||||
docketEntryId: Joi.number().integer().required(),
|
||||
title: Joi.string().required().max(500),
|
||||
summary: Joi.string().optional().allow(''),
|
||||
notes: Joi.string().optional().allow(''),
|
||||
displayOrder: Joi.number().integer().min(0).optional(),
|
||||
});
|
||||
|
||||
const updateDocumentSchema = Joi.object({
|
||||
title: Joi.string().optional().max(500),
|
||||
summary: Joi.string().optional().allow(''),
|
||||
notes: Joi.string().optional().allow(''),
|
||||
displayOrder: Joi.number().integer().min(0).optional(),
|
||||
});
|
||||
|
||||
// Public endpoint - Download document
|
||||
router.get('/:id/download', asyncHandler(async (req: Request, res: Response) => {
|
||||
const documentId = parseInt(req.params['id'] || '');
|
||||
|
||||
if (isNaN(documentId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid document ID',
|
||||
});
|
||||
}
|
||||
|
||||
const document = await prisma.document.findUnique({
|
||||
where: { id: documentId },
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
|
||||
const filePath = document.filePath;
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'File not found on server',
|
||||
});
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', document.mimeType);
|
||||
res.setHeader('Content-Disposition', `inline; filename="${document.originalFilename}"`);
|
||||
|
||||
const fileStream = fs.createReadStream(filePath);
|
||||
fileStream.pipe(res);
|
||||
}));
|
||||
|
||||
// Admin endpoint - Upload document
|
||||
router.post('/upload', authenticateToken, upload.single('file'), asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'No file uploaded',
|
||||
});
|
||||
}
|
||||
|
||||
const { error, value } = createDocumentSchema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
// Clean up uploaded file if validation fails
|
||||
fs.unlinkSync(req.file.path);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
const { docketEntryId, title, summary, notes, displayOrder } = value;
|
||||
|
||||
// Verify docket entry exists
|
||||
const docketEntry = await prisma.docketEntry.findUnique({
|
||||
where: { id: docketEntryId },
|
||||
});
|
||||
|
||||
if (!docketEntry) {
|
||||
// Clean up uploaded file
|
||||
fs.unlinkSync(req.file.path);
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found',
|
||||
});
|
||||
}
|
||||
|
||||
// Get next display order if not provided
|
||||
let finalDisplayOrder = displayOrder;
|
||||
if (finalDisplayOrder === undefined) {
|
||||
const lastDocument = await prisma.document.findFirst({
|
||||
where: { docketEntryId },
|
||||
orderBy: { displayOrder: 'desc' },
|
||||
});
|
||||
finalDisplayOrder = lastDocument ? lastDocument.displayOrder + 1 : 0;
|
||||
}
|
||||
|
||||
const document = await prisma.document.create({
|
||||
data: {
|
||||
docketEntryId,
|
||||
title,
|
||||
originalFilename: req.file.originalname,
|
||||
storedFilename: req.file.filename,
|
||||
filePath: req.file.path,
|
||||
fileSize: req.file.size,
|
||||
mimeType: req.file.mimetype,
|
||||
summary: summary || '',
|
||||
notes: notes || '',
|
||||
displayOrder: finalDisplayOrder,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'Document uploaded successfully',
|
||||
document,
|
||||
});
|
||||
}));
|
||||
|
||||
// Admin endpoint - Update document metadata
|
||||
router.put('/:id', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const documentId = parseInt(req.params['id'] || '');
|
||||
|
||||
if (isNaN(documentId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid document ID',
|
||||
});
|
||||
}
|
||||
|
||||
const { error, value } = updateDocumentSchema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const document = await prisma.document.update({
|
||||
where: { id: documentId },
|
||||
data: value,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Document updated successfully',
|
||||
document,
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
|
||||
// Admin endpoint - Delete document
|
||||
router.delete('/:id', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const documentId = parseInt(req.params['id'] || '');
|
||||
|
||||
if (isNaN(documentId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid document ID',
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const document = await prisma.document.findUnique({
|
||||
where: { id: documentId },
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
|
||||
// Delete the file from filesystem
|
||||
if (fs.existsSync(document.filePath)) {
|
||||
fs.unlinkSync(document.filePath);
|
||||
}
|
||||
|
||||
// Delete from database
|
||||
await prisma.document.delete({
|
||||
where: { id: documentId },
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Document deleted successfully',
|
||||
});
|
||||
} catch (error: any) {
|
||||
if (error.code === 'P2025') {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}));
|
||||
|
||||
export default router;
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import { prisma } from '../index';
|
||||
import { redis } from '../index';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Check database connection
|
||||
await prisma.$queryRaw`SELECT 1`;
|
||||
|
||||
// Check Redis connection
|
||||
await redis.ping();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'API is healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
services: {
|
||||
database: 'connected',
|
||||
redis: 'connected',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(503).json({
|
||||
success: false,
|
||||
message: 'Service unavailable',
|
||||
timestamp: new Date().toISOString(),
|
||||
services: {
|
||||
database: 'disconnected',
|
||||
redis: 'disconnected',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
import { Router, Response } from 'express';
|
||||
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
||||
import { emailService } from '../services/emailService';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Manual email notification endpoint
|
||||
router.post('/send/:docketEntryId', authenticateToken, async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
const { docketEntryId } = req.params;
|
||||
|
||||
// Import prisma here to avoid circular dependency
|
||||
const { prisma } = await import('../index');
|
||||
|
||||
// Get the docket entry with details
|
||||
const docketEntry = await prisma.docketEntry.findUnique({
|
||||
where: { id: parseInt(docketEntryId) },
|
||||
include: {
|
||||
documents: {
|
||||
orderBy: { displayOrder: 'asc' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!docketEntry) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Docket entry not found'
|
||||
});
|
||||
}
|
||||
|
||||
// Send the email notification using the BCC functionality
|
||||
await emailService.notifySubscribersOfNewEntry(
|
||||
docketEntry.title,
|
||||
docketEntry.date.toISOString(),
|
||||
docketEntry.summary
|
||||
);
|
||||
|
||||
logger.info(`Manual email notification sent for docket entry: ${docketEntry.title}`, {
|
||||
docketEntryId: docketEntry.id,
|
||||
adminUser: req.user?.username
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Email notification sent successfully to all subscribers'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to send manual email notification:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to send email notification'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
import { Router, Request, Response } from 'express';
|
||||
import { prisma } from '../index';
|
||||
import { asyncHandler } from '../middleware/errorHandler';
|
||||
import { authenticateToken, AuthenticatedRequest } from '../middleware/auth';
|
||||
import Joi from 'joi';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Validation schemas
|
||||
const subscribeSchema = Joi.object({
|
||||
email: Joi.string().email().required(),
|
||||
});
|
||||
|
||||
// Subscribe to notifications
|
||||
router.post('/subscribe', asyncHandler(async (req: Request, res: Response) => {
|
||||
const { error, value } = subscribeSchema.validate(req.body);
|
||||
|
||||
if (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.details[0].message,
|
||||
});
|
||||
}
|
||||
|
||||
const { email } = value;
|
||||
|
||||
// Check if already subscribed
|
||||
const existing = await prisma.subscription.findUnique({
|
||||
where: { email },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
if (existing.isActive) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Email is already subscribed',
|
||||
});
|
||||
} else {
|
||||
// Reactivate subscription
|
||||
await prisma.subscription.update({
|
||||
where: { email },
|
||||
data: { isActive: true },
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Create new subscription
|
||||
await prisma.subscription.create({
|
||||
data: {
|
||||
email,
|
||||
unsubscribeToken: uuidv4(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Successfully subscribed to notifications',
|
||||
});
|
||||
}));
|
||||
|
||||
// Admin endpoint - Get all subscribers
|
||||
router.get('/', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const subscribers = await prisma.subscription.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
isActive: true,
|
||||
createdAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
const stats = {
|
||||
total: subscribers.length,
|
||||
active: subscribers.filter(sub => sub.isActive).length,
|
||||
inactive: subscribers.filter(sub => !sub.isActive).length,
|
||||
};
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
subscribers,
|
||||
stats,
|
||||
});
|
||||
}));
|
||||
|
||||
// Admin endpoint - Toggle subscriber status
|
||||
router.put('/:id/toggle', authenticateToken, asyncHandler(async (req: AuthenticatedRequest, res: Response) => {
|
||||
const subscriberId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(subscriberId)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Invalid subscriber ID',
|
||||
});
|
||||
}
|
||||
|
||||
const subscriber = await prisma.subscription.findUnique({
|
||||
where: { id: subscriberId },
|
||||
});
|
||||
|
||||
if (!subscriber) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Subscriber not found',
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await prisma.subscription.update({
|
||||
where: { id: subscriberId },
|
||||
data: { isActive: !subscriber.isActive },
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Subscriber ${updated.isActive ? 'activated' : 'deactivated'} successfully`,
|
||||
subscriber: updated,
|
||||
});
|
||||
}));
|
||||
|
||||
export default router;
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
import { logger } from '../utils/logger';
|
||||
import nodemailer from 'nodemailer';
|
||||
|
||||
interface EmailOptions {
|
||||
to: string;
|
||||
bcc?: string[];
|
||||
subject: string;
|
||||
html: string;
|
||||
}
|
||||
|
||||
export class EmailService {
|
||||
private static instance: EmailService;
|
||||
private transporter: nodemailer.Transporter;
|
||||
|
||||
private constructor() {
|
||||
// Initialize Gmail SMTP transporter
|
||||
this.transporter = nodemailer.createTransport({
|
||||
host: process.env['SMTP_HOST'] || 'smtp.gmail.com',
|
||||
port: parseInt(process.env['SMTP_PORT'] || '587'),
|
||||
secure: process.env['SMTP_SECURE'] === 'true', // false for 587, true for 465
|
||||
auth: {
|
||||
user: process.env['GOOGLE_EMAIL'],
|
||||
pass: process.env['GOOGLE_APP_PASSWORD'],
|
||||
},
|
||||
});
|
||||
|
||||
// Verify SMTP connection
|
||||
this.verifyConnection();
|
||||
}
|
||||
|
||||
public static getInstance(): EmailService {
|
||||
if (!EmailService.instance) {
|
||||
EmailService.instance = new EmailService();
|
||||
}
|
||||
return EmailService.instance;
|
||||
}
|
||||
|
||||
private async verifyConnection(): Promise<void> {
|
||||
try {
|
||||
await this.transporter.verify();
|
||||
logger.info('SMTP connection verified successfully');
|
||||
} catch (error) {
|
||||
logger.error('SMTP connection failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async sendEmail(options: EmailOptions): Promise<boolean> {
|
||||
try {
|
||||
const mailOptions: any = {
|
||||
from: {
|
||||
name: 'Court Docket System',
|
||||
address: process.env['GOOGLE_EMAIL'] || 'system@deafgain.org',
|
||||
},
|
||||
to: options.to,
|
||||
subject: options.subject,
|
||||
html: options.html,
|
||||
};
|
||||
|
||||
// Add BCC if provided
|
||||
if (options.bcc && options.bcc.length > 0) {
|
||||
mailOptions.bcc = options.bcc;
|
||||
}
|
||||
|
||||
const result = await this.transporter.sendMail(mailOptions);
|
||||
|
||||
logger.info('Email sent successfully:', {
|
||||
to: options.to,
|
||||
bcc: options.bcc ? `${options.bcc.length} recipients` : 'none',
|
||||
subject: options.subject,
|
||||
messageId: result.messageId,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Failed to send email:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async notifySubscribersOfNewEntry(entryTitle: string, entryDate: string, entrySummary: string): Promise<void> {
|
||||
try {
|
||||
// Get all active subscribers
|
||||
const { prisma } = await import('../index');
|
||||
const subscribers = await prisma.subscription.findMany({
|
||||
where: { isActive: true },
|
||||
});
|
||||
|
||||
if (subscribers.length === 0) {
|
||||
logger.info('No active subscribers to notify');
|
||||
return;
|
||||
}
|
||||
|
||||
const subject = `New Court Filing: ${entryTitle}`;
|
||||
const html = this.generateNotificationEmail(entryTitle, entryDate, entrySummary);
|
||||
const subscriberEmails = subscribers.map((subscriber: any) => subscriber.email);
|
||||
|
||||
// Send single email with all subscribers as BCC
|
||||
const success = await this.sendEmail({
|
||||
to: process.env['GOOGLE_EMAIL'] || 'system@deafgain.org',
|
||||
bcc: subscriberEmails,
|
||||
subject,
|
||||
html,
|
||||
});
|
||||
|
||||
if (success) {
|
||||
logger.info(`Email notification sent successfully to ${subscriberEmails.length} subscribers via BCC`);
|
||||
} else {
|
||||
logger.error(`Failed to send BCC email notification to ${subscriberEmails.length} subscribers`);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to notify subscribers:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private generateNotificationEmail(title: string, date: string, summary: string): string {
|
||||
const formattedDate = new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>New Court Filing Notification</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
background-color: #1f2937;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.content {
|
||||
background-color: #f9fafb;
|
||||
padding: 30px;
|
||||
border-radius: 0 0 8px 8px;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
.filing-info {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
margin: 20px 0;
|
||||
border-left: 4px solid #3b82f6;
|
||||
}
|
||||
.date {
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #1f2937;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.summary {
|
||||
color: #4b5563;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #3b82f6;
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Eliza Kragh v. Montana Association of the Deaf</h1>
|
||||
<p>New Court Filing Notification</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>A new filing has been added to the court docket:</p>
|
||||
|
||||
<div class="filing-info">
|
||||
<div class="date">${formattedDate}</div>
|
||||
<div class="title">${title}</div>
|
||||
<div class="summary">${summary}</div>
|
||||
</div>
|
||||
|
||||
<p>You can view the complete docket and any associated documents by visiting the <a href="https://mad-lawsuit.org/" style="color: #3b82f6; text-decoration: none;">website</a>:</p>
|
||||
|
||||
<a href="https://mad-lawsuit.org/" class="button">
|
||||
View Court Docket
|
||||
</a>
|
||||
|
||||
<div class="footer">
|
||||
<p>You are receiving this email because you subscribed to notifications for this case.</p>
|
||||
<p>This is an automated notification from the court docket system.</p>
|
||||
<p>To unsubscribe, please send an email to eliza.kragh@gmail.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export const emailService = EmailService.getInstance();
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
import winston from 'winston';
|
||||
|
||||
const logFormat = winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.json()
|
||||
);
|
||||
|
||||
export const logger = winston.createLogger({
|
||||
level: process.env.LOG_LEVEL || 'info',
|
||||
format: logFormat,
|
||||
defaultMeta: { service: 'docket-api' },
|
||||
transports: [
|
||||
new winston.transports.File({
|
||||
filename: 'logs/error.log',
|
||||
level: 'error',
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5,
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'logs/combined.log',
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// If we're not in production, log to the console as well
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
logger.add(new winston.transports.Console({
|
||||
format: winston.format.combine(
|
||||
winston.format.colorize(),
|
||||
winston.format.simple()
|
||||
)
|
||||
}));
|
||||
}
|
||||
|
||||
export default logger;
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"noImplicitAny": false,
|
||||
"noImplicitReturns": false,
|
||||
"noImplicitThis": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"noImplicitOverride": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"allowUnusedLabels": true,
|
||||
"allowUnreachableCode": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
23
bootstrap/app.php
Normal file
23
bootstrap/app.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
$middleware->web(append: [
|
||||
\App\Http\Middleware\HandleInertiaRequests::class,
|
||||
\Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
|
||||
]);
|
||||
|
||||
//
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
})->create();
|
||||
2
bootstrap/cache/.gitignore
vendored
Normal file
2
bootstrap/cache/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
5
bootstrap/providers.php
Normal file
5
bootstrap/providers.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
];
|
||||
|
|
@ -1,10 +1,82 @@
|
|||
# Active Context - Current Work Status
|
||||
|
||||
## Current Task: PRODUCTION SERVER MIGRATION COMPLETED ✅
|
||||
## Current Task: v2.0 LARAVEL + INERTIA + VUE FOUNDATION COMPLETE ✅
|
||||
**MAD Lawsuit Website v2.0 - Complete Technology Stack Migration**:
|
||||
- **v1.0 Status**: Fully operational at https://mad-lawsuit.org (Next.js + Express + React)
|
||||
- **v2.0 Goal**: Rebuild with Laravel + Inertia + Vue + TypeScript
|
||||
- **Current Phase**: Foundation complete, home page working, ready for features
|
||||
- **Status**: TESTED LOCALLY - WEBSITE RENDERING CORRECTLY ✅
|
||||
|
||||
### v2.0 Technology Stack
|
||||
**Backend**:
|
||||
- **Framework**: Laravel 12.43.1 (latest stable)
|
||||
- **PHP**: 8.3.28
|
||||
- **Database**: PostgreSQL (will migrate from v1.0)
|
||||
- **ORM**: Eloquent (replacing Prisma)
|
||||
- **Auth**: Laravel Sanctum
|
||||
- **API**: Inertia.js server-side
|
||||
|
||||
**Frontend**:
|
||||
- **Framework**: Vue 3 + TypeScript
|
||||
- **Routing**: Inertia.js (SSR-like experience)
|
||||
- **Styling**: Tailwind CSS 3.x
|
||||
- **Build Tool**: Vite 7.x
|
||||
- **Components**: Vue SFC (Single File Components)
|
||||
|
||||
**Development Environment**:
|
||||
- **PHP**: Installed via Homebrew (8.3.28)
|
||||
- **Composer**: 2.9.2
|
||||
- **Node**: Latest stable
|
||||
- **NPM**: With legacy-peer-deps for Vite compatibility
|
||||
|
||||
### v2.0 Progress Checklist
|
||||
- [x] Install PHP 8.3 and Composer locally
|
||||
- [x] Create Laravel 12 project
|
||||
- [x] Install Laravel Breeze with Vue + Inertia + TypeScript
|
||||
- [x] Resolve NPM dependency conflicts (Vite 7 compatibility)
|
||||
- [x] Install all NPM packages successfully
|
||||
- [x] Create database migrations (docket_entries, documents, subscriptions, admin_users)
|
||||
- [x] Create Eloquent models with relationships
|
||||
- [x] Run migrations successfully (SQLite)
|
||||
- [x] Create HomeController with Inertia
|
||||
- [x] Build Vue home page component matching v1.0 design
|
||||
- [x] Test locally with development servers (php artisan serve + npm run dev)
|
||||
- [x] Verify website rendering correctly
|
||||
- [ ] Implement email subscription API
|
||||
- [ ] Add document download functionality
|
||||
- [ ] Configure PostgreSQL connection (for production)
|
||||
- [ ] Build admin authentication
|
||||
- [ ] Implement admin dashboard (CRUD operations)
|
||||
- [ ] Set up file storage for PDFs
|
||||
- [ ] Implement email notifications
|
||||
- [ ] Export v1.0 production data
|
||||
- [ ] Import data into v2.0
|
||||
- [ ] Deploy to v2.mad-lawsuit.org for testing
|
||||
|
||||
### v2.0 Design Specifications
|
||||
**Complete v1.0 documentation captured**:
|
||||
- ✅ Visual design (colors, typography, layout)
|
||||
- ✅ Component specifications (forms, cards, buttons)
|
||||
- ✅ Functional requirements (interactions, animations)
|
||||
- ✅ Data structures (63 entries, 63 PDFs, 28 subscribers)
|
||||
- ✅ Admin dashboard specs (complete CRUD operations)
|
||||
- ✅ Production data verified from live database
|
||||
|
||||
**Reference Document**: `cline_docs/v1_design_specifications.md` (400+ lines)
|
||||
|
||||
### Next Immediate Steps
|
||||
1. **Create Laravel Migrations** - Match v1.0 Prisma schema
|
||||
2. **Configure .env** - PostgreSQL connection settings
|
||||
3. **Build Vue Components** - Replicate v1.0 design exactly
|
||||
4. **Implement Routes** - Public and admin routes
|
||||
5. **Test Locally** - Verify all functionality works
|
||||
|
||||
## Previous Task: v1.0 PRODUCTION SERVER MIGRATION COMPLETED ✅
|
||||
**MAD Lawsuit Website Successfully Migrated to New Infrastructure**:
|
||||
- **Old Server**: chrishaulmark.com (DigitalOcean VPS - being decommissioned)
|
||||
- **New Server**: 10.4.0.205 (Dedicated server behind NAT)
|
||||
- **Status**: FULLY OPERATIONAL WITH ALL DATA MIGRATED ✅
|
||||
- **v1.0 Tagged**: Git tag `v1.0` created and pushed to Gitea
|
||||
|
||||
### Migration Implementation Details
|
||||
**Infrastructure Changes**:
|
||||
|
|
|
|||
505
cline_docs/v1_design_specifications.md
Normal file
505
cline_docs/v1_design_specifications.md
Normal file
|
|
@ -0,0 +1,505 @@
|
|||
# v1.0 Design Specifications - For v2.0 Replication
|
||||
|
||||
## Overview
|
||||
This document captures the complete design and functionality of the v1.0 website (Next.js + Express) to guide the v2.0 Laravel + Inertia + Vue implementation.
|
||||
|
||||
## Visual Design
|
||||
|
||||
### Color Scheme
|
||||
- **Primary Background**: Dark slate blue/gray (#3f4a5c, #4a5568)
|
||||
- **Secondary Background**: Medium gray/brown (#6b6b6b, #7a7a7a)
|
||||
- **Card Background**: White (#ffffff)
|
||||
- **Text Primary**: Black (#000000)
|
||||
- **Text Secondary**: Gray (#6b7280, #9ca3af)
|
||||
- **Accent Green**: Light green for status badges (#d1fae5, #10b981)
|
||||
- **Button Green**: Medium green (#6b9080, #5a8070)
|
||||
- **Button Hover**: Darker green (#4a6050)
|
||||
|
||||
### Typography
|
||||
- **Main Heading**: Large, bold, white text - "ELIZABETH KRAGH v. MONTANA ASSOCIATION OF THE DEAF"
|
||||
- **Subheading**: Light gray text - "Court Docket & Legal Documents"
|
||||
- **Section Headers**: Bold, centered - "Case Information", "Court Docket Entries"
|
||||
- **Body Text**: Standard weight, readable size
|
||||
- **Dates**: Small, gray text above entry titles
|
||||
|
||||
### Layout Structure
|
||||
|
||||
#### 1. Hero Section (Top)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Dark Blue Background │
|
||||
│ │
|
||||
│ ELIZABETH KRAGH │
|
||||
│ v. │
|
||||
│ MONTANA ASSOCIATION OF THE DEAF │
|
||||
│ │
|
||||
│ Court Docket & Legal Documents │
|
||||
│ │
|
||||
│ Stay Informed │
|
||||
│ [Email Input] [Subscribe to Updates Button] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 2. Case Information Card
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Case Information │
|
||||
│ │
|
||||
│ Case Title │
|
||||
│ Elizabeth Kragh v. Montana Association of the Deaf │
|
||||
│ │
|
||||
│ Status │
|
||||
│ [Active Litigation Badge] │
|
||||
│ │
|
||||
│ Last Updated │
|
||||
│ November 12, 2025 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 3. Court Docket Entries Section
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Court Docket Entries │
|
||||
│ │
|
||||
│ Chronological record of all court filings and │
|
||||
│ proceedings │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ [#] May 7, 2025 [▼] │ │
|
||||
│ │ Complaint Exhibits │ │
|
||||
│ │ │ │
|
||||
│ │ Brief Summary: │ │
|
||||
│ │ The exhibits show the Montana Association... │ │
|
||||
│ │ │ │
|
||||
│ │ [View PDF Button] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ [#] May 7, 2025 [▼] │ │
|
||||
│ │ Affidavit in Support of Complaint... │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ... (more entries) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Component Specifications
|
||||
|
||||
### 1. Email Subscription Form
|
||||
- **Input Field**:
|
||||
- Placeholder: "Enter your email address"
|
||||
- White background
|
||||
- Rounded corners
|
||||
- Border styling
|
||||
- **Button**:
|
||||
- Text: "Subscribe to Updates"
|
||||
- Green background (#6b9080)
|
||||
- White text
|
||||
- Icon: Mail/envelope icon
|
||||
- Hover effect: Darker green
|
||||
|
||||
### 2. Case Information Card
|
||||
- **Container**: White background, rounded corners, shadow
|
||||
- **Labels**: Gray text, smaller font
|
||||
- **Values**: Black text, larger font, bold
|
||||
- **Status Badge**:
|
||||
- Light green background (#d1fae5)
|
||||
- Dark green text (#10b981)
|
||||
- Rounded pill shape
|
||||
- Text: "Active Litigation"
|
||||
|
||||
### 3. Docket Entry Cards
|
||||
- **Container**: White background, rounded corners, shadow
|
||||
- **Number Badge**:
|
||||
- Dark circle with white number
|
||||
- Positioned on left side
|
||||
- **Date**: Small gray text at top
|
||||
- **Title**: Bold black text, larger font
|
||||
- **Expand/Collapse Icon**: Chevron down/up on right
|
||||
- **Expanded State Shows**:
|
||||
- "Brief Summary:" label
|
||||
- Summary text in paragraph
|
||||
- "View PDF" button (green, white text)
|
||||
- **Hover Effect**: Slight shadow increase
|
||||
|
||||
### 4. View PDF Button
|
||||
- **Style**: Green background, white text, rounded
|
||||
- **Icon**: Document/file icon
|
||||
- **Text**: "View PDF"
|
||||
- **Action**: Opens PDF in new tab or viewer
|
||||
|
||||
## Functional Requirements
|
||||
|
||||
### 1. Email Subscription
|
||||
- Input validation for email format
|
||||
- Subscribe button sends POST request to `/api/subscriptions/subscribe`
|
||||
- Success message displayed after subscription
|
||||
- Error handling for duplicate emails
|
||||
|
||||
### 2. Docket Entry Expansion
|
||||
- Click anywhere on entry card to expand/collapse
|
||||
- Smooth animation for expand/collapse
|
||||
- Only one entry expanded at a time (accordion behavior)
|
||||
- Chevron icon rotates on expand/collapse
|
||||
|
||||
### 3. PDF Viewing
|
||||
- "View PDF" button opens document
|
||||
- PDFs served from `/api/documents/{id}/download`
|
||||
- Opens in new browser tab
|
||||
- Proper MIME type handling
|
||||
|
||||
### 4. Responsive Design
|
||||
- Mobile-friendly layout
|
||||
- Cards stack vertically on small screens
|
||||
- Email form adjusts for mobile
|
||||
- Touch-friendly tap targets
|
||||
|
||||
## Data Structure
|
||||
|
||||
### Docket Entry Object
|
||||
```typescript
|
||||
{
|
||||
id: number
|
||||
date: Date
|
||||
title: string
|
||||
summary: string
|
||||
notes: string (optional)
|
||||
documents: Document[]
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
```
|
||||
|
||||
### Document Object
|
||||
```typescript
|
||||
{
|
||||
id: number
|
||||
docketEntryId: number
|
||||
title: string
|
||||
originalFilename: string
|
||||
storedFilename: string
|
||||
filePath: string
|
||||
fileSize: number
|
||||
mimeType: string
|
||||
summary: string (optional)
|
||||
notes: string (optional)
|
||||
displayOrder: number
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
```
|
||||
|
||||
### Subscription Object
|
||||
```typescript
|
||||
{
|
||||
id: number
|
||||
email: string
|
||||
isActive: boolean
|
||||
unsubscribeToken: string
|
||||
createdAt: Date
|
||||
}
|
||||
```
|
||||
|
||||
## API Endpoints (for v2.0 Implementation)
|
||||
|
||||
### Public Endpoints
|
||||
- `GET /api/docket-entries` - Fetch all docket entries with documents
|
||||
- `POST /api/subscriptions/subscribe` - Subscribe to email updates
|
||||
- `GET /api/documents/{id}/download` - Download/view PDF document
|
||||
- `GET /api/health` - Health check
|
||||
|
||||
### Admin Endpoints (Protected)
|
||||
- `POST /api/auth/login` - Admin login
|
||||
- `GET /api/auth/verify` - Verify JWT token
|
||||
- `GET /api/docket-entries/{id}` - Get single entry
|
||||
- `POST /api/docket-entries` - Create new entry (triggers email)
|
||||
- `PUT /api/docket-entries/{id}` - Update entry
|
||||
- `DELETE /api/docket-entries/{id}` - Delete entry
|
||||
- `POST /api/documents/upload` - Upload PDF document
|
||||
|
||||
## Animation & Interactions
|
||||
|
||||
### Hover Effects
|
||||
- **Buttons**: Darken background color
|
||||
- **Cards**: Increase shadow, slight lift effect
|
||||
- **Links**: Underline or color change
|
||||
|
||||
### Transitions
|
||||
- **Card Expansion**: 300ms ease-in-out
|
||||
- **Button Hover**: 200ms ease
|
||||
- **Page Load**: Fade in effect
|
||||
|
||||
### Loading States
|
||||
- Spinner or skeleton screens while fetching data
|
||||
- Disabled state for buttons during submission
|
||||
|
||||
## Footer
|
||||
- **Text**: "Designed by DeafGain LLC"
|
||||
- **Link**: http://deafgain.org
|
||||
- **Style**:
|
||||
- Dark background matching hero section
|
||||
- Gray text with yellow link (#fbbf24)
|
||||
- Centered alignment
|
||||
- Border top separator
|
||||
|
||||
## Notes for v2.0 Implementation
|
||||
|
||||
### Key Differences to Maintain
|
||||
1. **Same visual appearance** - colors, fonts, spacing
|
||||
2. **Same user interactions** - expand/collapse, email subscription
|
||||
3. **Same data structure** - compatible with v1.0 database export
|
||||
4. **Same API contract** - frontend expects same response format
|
||||
|
||||
### Technology Mapping
|
||||
- **Next.js Pages** → **Inertia.js Pages (Vue)**
|
||||
- **React Components** → **Vue Components**
|
||||
- **Express Routes** → **Laravel Routes**
|
||||
- **Prisma ORM** → **Laravel Eloquent ORM**
|
||||
- **JWT Auth** → **Laravel Sanctum/Passport**
|
||||
- **Tailwind CSS** → **Tailwind CSS** (same)
|
||||
|
||||
### Priority Features for v2.0
|
||||
1. ✅ Public docket viewing (exact same layout)
|
||||
2. ✅ Email subscription (same functionality)
|
||||
3. ✅ PDF document viewing (same behavior)
|
||||
4. ✅ Admin authentication (Laravel-based)
|
||||
5. ✅ Admin dashboard (CRUD operations)
|
||||
6. ✅ Document upload (with validation)
|
||||
7. ✅ Email notifications (on new entries)
|
||||
|
||||
## Current Production Data (Verified from Live Database)
|
||||
|
||||
### Database Statistics
|
||||
- **63 docket entries** with dates from May 7, 2025 to November 12, 2025
|
||||
- **63 PDF documents** (158MB total, 1 document per entry)
|
||||
- **28 email subscribers** (27 active, 1 inactive)
|
||||
- **1 admin user** with secure authentication
|
||||
- **Active litigation status**
|
||||
- **Last updated**: November 12, 2025
|
||||
|
||||
### Sample Docket Entries (Most Recent)
|
||||
```
|
||||
ID: 73 | Date: Nov 12, 2025
|
||||
Title: Reply Brief in Support of Motion to Modify Scheduling Order
|
||||
Summary: Plaintiff Elizabeth Kragh asks the judge to extend the October 31 deadline...
|
||||
|
||||
ID: 75 | Date: Nov 12, 2025
|
||||
Title: Reply Brief in Support of Motion for Additional Discovery Time
|
||||
Summary: Plaintiff Elizabeth Kragh responds to MAD's opposition, arguing the organization admits...
|
||||
|
||||
ID: 76 | Date: Nov 12, 2025
|
||||
Title: Reply Brief in Support of Motion to Compel Discovery
|
||||
Summary: Plaintiff Elizabeth Kragh responds to MAD's opposition, emphasizing that MAD admits...
|
||||
|
||||
ID: 72 | Date: Oct 29, 2025
|
||||
Title: Order Granting Motion to Extend Time For Filing Reply Briefs
|
||||
Summary: This is the court's electronic filing receipt confirming that Judge Tara Elliott granted...
|
||||
|
||||
ID: 71 | Date: Oct 28, 2025
|
||||
Title: Motion to Extend Time for Filing Reply Briefs
|
||||
Summary: This motion requests more time to file reply briefs in response to the Montana Association...
|
||||
```
|
||||
|
||||
### Sample Documents
|
||||
```
|
||||
ID: 4 | Entry: 1 | Title: 05-07-25-affidavit | Size: 2.8MB
|
||||
ID: 5 | Entry: 5 | Title: 05-07-25-complaint | Size: 12MB
|
||||
ID: 6 | Entry: 6 | Title: 05-07-25-exhibits-complaint | Size: 40MB
|
||||
ID: 8 | Entry: 8 | Title: 05-16-25-amended-cert-of-service | Size: 848KB
|
||||
ID: 9 | Entry: 9 | Title: 05-14-25-cert-service | Size: 823KB
|
||||
```
|
||||
|
||||
### Email Subscribers (28 total)
|
||||
- **Most recent**: quarks.tattoo-09@icloud.com (Nov 19, 2025)
|
||||
- **Includes**: eliza.kragh@gmail.com, chris@sigd.net, and 26 other supporters
|
||||
- **Active**: 27 subscribers receiving notifications
|
||||
- **Inactive**: 1 subscriber (mike.crago@gmail.com)
|
||||
|
||||
### File Storage
|
||||
- **Location**: `/docker/websites/mad-lawsuit/uploads/`
|
||||
- **Naming**: UUID-based (e.g., `004f5cd2-d600-4125-86da-bca491183fcb.pdf`)
|
||||
- **Total size**: 158MB across 63 PDF files
|
||||
- **File sizes**: Range from 441KB to 40MB
|
||||
- **Format**: All application/pdf MIME type
|
||||
|
||||
## Admin Dashboard (v1.0 Implementation)
|
||||
|
||||
### Admin Authentication
|
||||
- **Route**: `/admin`
|
||||
- **Login Form**:
|
||||
- Username input field
|
||||
- Password input field (masked)
|
||||
- "Login" button
|
||||
- Error message display for failed attempts
|
||||
- **Authentication**: JWT-based with bcrypt password hashing
|
||||
- **Session**: Token stored in localStorage/cookies
|
||||
- **Protected Routes**: All `/admin/*` routes require valid JWT
|
||||
|
||||
### Admin Dashboard Layout
|
||||
**Route**: `/admin/dashboard`
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Header: "Admin Dashboard" | [Logout Button] │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Navigation Tabs: │
|
||||
│ [Docket Entries] [Upload Document] [Subscribers] │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Docket Entries Management: │
|
||||
│ │
|
||||
│ [+ Create New Entry Button] │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ Entry #73 | Nov 12, 2025 │ │
|
||||
│ │ Reply Brief in Support of Motion... │ │
|
||||
│ │ [Edit] [Delete] [View] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ Entry #75 | Nov 12, 2025 │ │
|
||||
│ │ Reply Brief in Support of Motion... │ │
|
||||
│ │ [Edit] [Delete] [View] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ... (pagination for all 63 entries) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Create/Edit Docket Entry Form
|
||||
**Fields**:
|
||||
- **Date**: Date picker (required)
|
||||
- **Title**: Text input, max 500 characters (required)
|
||||
- **Summary**: Textarea, rich text (required)
|
||||
- **Notes**: Textarea, optional
|
||||
- **Document Upload**: File input (PDF only, max 10MB)
|
||||
- Shows file name after selection
|
||||
- Validates file type and size
|
||||
- Displays upload progress
|
||||
- **Actions**:
|
||||
- [Save Entry] button (green)
|
||||
- [Cancel] button (gray)
|
||||
- [Delete Entry] button (red, edit mode only)
|
||||
|
||||
**Behavior**:
|
||||
- Creating new entry triggers email notification to all active subscribers
|
||||
- Editing entry does NOT trigger email (only new entries)
|
||||
- Deleting entry also deletes associated document file
|
||||
- Form validation before submission
|
||||
- Success/error messages displayed
|
||||
|
||||
### Upload Document Page
|
||||
**Route**: `/admin/upload`
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Upload Document to Existing Entry │
|
||||
│ │
|
||||
│ Select Docket Entry: │
|
||||
│ [Dropdown: All entries with date and title] │
|
||||
│ │
|
||||
│ Document Title: │
|
||||
│ [Text input] │
|
||||
│ │
|
||||
│ Choose PDF File: │
|
||||
│ [File input] [Browse...] │
|
||||
│ │
|
||||
│ Summary (optional): │
|
||||
│ [Textarea] │
|
||||
│ │
|
||||
│ Notes (optional): │
|
||||
│ [Textarea] │
|
||||
│ │
|
||||
│ [Upload Document Button] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Validation**:
|
||||
- PDF files only (application/pdf MIME type)
|
||||
- Maximum file size: 10MB
|
||||
- Required: docket_entry_id, title, file
|
||||
- Optional: summary, notes
|
||||
|
||||
### Subscribers Management Page
|
||||
**Route**: `/admin/subscribers`
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Email Subscribers (28 total, 27 active) │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ quarks.tattoo-09@icloud.com │ │
|
||||
│ │ Subscribed: Nov 19, 2025 | Status: Active │ │
|
||||
│ │ [Deactivate] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ letsgetonacid222@gmail.com │ │
|
||||
│ │ Subscribed: Oct 28, 2025 | Status: Active │ │
|
||||
│ │ [Deactivate] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ mike.crago@gmail.com │ │
|
||||
│ │ Subscribed: Jul 14, 2025 | Status: Inactive │ │
|
||||
│ │ [Activate] │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ... (all 28 subscribers listed) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- View all subscribers with email, subscription date, and status
|
||||
- Toggle active/inactive status
|
||||
- Search/filter subscribers
|
||||
- Export subscriber list (optional)
|
||||
- No delete function (preserve data integrity)
|
||||
|
||||
### Admin Dashboard Features Summary
|
||||
1. **Authentication**: Secure JWT-based login
|
||||
2. **Docket Management**: Full CRUD operations on entries
|
||||
3. **Document Upload**: PDF upload with validation
|
||||
4. **Subscriber Management**: View and manage email list
|
||||
5. **Email Notifications**: Automatic on new entry creation
|
||||
6. **Responsive Design**: Works on desktop and tablet
|
||||
7. **Error Handling**: Comprehensive validation and error messages
|
||||
8. **Logout**: Clear session and redirect to login
|
||||
|
||||
## Data Export Strategy for v2.0 Migration
|
||||
|
||||
### Database Export
|
||||
```bash
|
||||
# Export all tables as SQL dump
|
||||
ssh chaulmark@10.4.0.205
|
||||
cd ~/websites/mad-lawsuit.org
|
||||
docker compose exec postgres pg_dump -U docket_user -d docket_db > mad-lawsuit-v1-export.sql
|
||||
|
||||
# Or export as JSON for Laravel seeders
|
||||
docker compose exec postgres psql -U docket_user -d docket_db -c "COPY (SELECT * FROM docket_entries) TO STDOUT WITH CSV HEADER" > docket_entries.csv
|
||||
docker compose exec postgres psql -U docket_user -d docket_db -c "COPY (SELECT * FROM documents) TO STDOUT WITH CSV HEADER" > documents.csv
|
||||
docker compose exec postgres psql -U docket_user -d docket_db -c "COPY (SELECT * FROM subscriptions) TO STDOUT WITH CSV HEADER" > subscriptions.csv
|
||||
```
|
||||
|
||||
### File Export
|
||||
```bash
|
||||
# Copy all PDF files
|
||||
rsync -avz chaulmark@10.4.0.205:/docker/websites/mad-lawsuit/uploads/ ./v1-uploads/
|
||||
# Total: 158MB, 63 files
|
||||
```
|
||||
|
||||
### Import into v2.0 Laravel
|
||||
1. **Database**: Create Laravel migrations matching v1.0 schema
|
||||
2. **Seeders**: Import CSV data into Laravel database
|
||||
3. **Storage**: Copy PDFs to Laravel storage/app/public/documents/
|
||||
4. **Mapping**: Update file_path in documents table to Laravel paths
|
||||
|
||||
This specification ensures v2.0 will be a pixel-perfect, functionally identical replacement for v1.0, just built with Laravel + Inertia + Vue instead of Next.js + Express.
|
||||
90
composer.json
Normal file
90
composer.json
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"$schema": "https://getcomposer.org/schema.json",
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The skeleton application for the Laravel framework.",
|
||||
"keywords": ["laravel", "framework"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"inertiajs/inertia-laravel": "^2.0",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"tightenco/ziggy": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/breeze": "^2.3",
|
||||
"laravel/pail": "^1.2.2",
|
||||
"laravel/pint": "^1.24",
|
||||
"laravel/sail": "^1.41",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"phpunit/phpunit": "^11.5.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"setup": [
|
||||
"composer install",
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
|
||||
"@php artisan key:generate",
|
||||
"@php artisan migrate --force",
|
||||
"npm install",
|
||||
"npm run build"
|
||||
],
|
||||
"dev": [
|
||||
"Composer\\Config::disableProcessTimeout",
|
||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
|
||||
],
|
||||
"test": [
|
||||
"@php artisan config:clear --ansi",
|
||||
"@php artisan test"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi",
|
||||
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
|
||||
"@php artisan migrate --graceful --ansi"
|
||||
],
|
||||
"pre-package-uninstall": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
126
config/app.php
Normal file
126
config/app.php
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application, which will be used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| other UI elements where an application name needs to be displayed.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| the application so that it's available within Artisan commands.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. The timezone
|
||||
| is set to "UTC" by default as it is suitable for most use cases.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by Laravel's translation / localization methods. This option can be
|
||||
| set to any locale for which you plan to have translation strings.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is utilized by Laravel's encryption services and should be set
|
||||
| to a random, 32 character string to ensure that all encrypted values
|
||||
| are secure. You should do this prior to deploying the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'previous_keys' => [
|
||||
...array_filter(
|
||||
explode(',', (string) env('APP_PREVIOUS_KEYS', ''))
|
||||
),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||
],
|
||||
|
||||
];
|
||||
115
config/auth.php
Normal file
115
config/auth.php
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default authentication "guard" and password
|
||||
| reset "broker" for your application. You may change these values
|
||||
| as required, but they're a perfect start for most applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => env('AUTH_GUARD', 'web'),
|
||||
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Guards
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, you may define every authentication guard for your application.
|
||||
| Of course, a great default configuration has been defined for you
|
||||
| which utilizes session storage plus the Eloquent user provider.
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| Supported: "session"
|
||||
|
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| All authentication guards have a user provider, which defines how the
|
||||
| users are actually retrieved out of your database or other storage
|
||||
| system used by the application. Typically, Eloquent is utilized.
|
||||
|
|
||||
| If you have multiple user tables or models you may configure multiple
|
||||
| providers to represent the model / table. These providers may then
|
||||
| be assigned to any extra authentication guards you have defined.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Resetting Passwords
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options specify the behavior of Laravel's password
|
||||
| reset functionality, including the table utilized for token storage
|
||||
| and the user provider that is invoked to actually retrieve users.
|
||||
|
|
||||
| The expiry time is the number of minutes that each reset token will be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
| The throttle setting is the number of seconds a user must wait before
|
||||
| generating more password reset tokens. This prevents the user from
|
||||
| quickly generating a very large amount of password reset tokens.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
|
||||
'expire' => 60,
|
||||
'throttle' => 60,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Confirmation Timeout
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the number of seconds before a password confirmation
|
||||
| window expires and users are asked to re-enter their password via the
|
||||
| confirmation screen. By default, the timeout lasts for three hours.
|
||||
|
|
||||
*/
|
||||
|
||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||
|
||||
];
|
||||
117
config/cache.php
Normal file
117
config/cache.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache store that will be used by the
|
||||
| framework. This connection is utilized if another isn't explicitly
|
||||
| specified when running a cache operation inside the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_STORE', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
| Supported drivers: "array", "database", "file", "memcached",
|
||||
| "redis", "dynamodb", "octane",
|
||||
| "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
'serialize' => false,
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_CACHE_CONNECTION'),
|
||||
'table' => env('DB_CACHE_TABLE', 'cache'),
|
||||
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
|
||||
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache/data'),
|
||||
'lock_path' => storage_path('framework/cache/data'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
|
||||
'sasl' => [
|
||||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
'port' => env('MEMCACHED_PORT', 11211),
|
||||
'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
|
||||
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
|
||||
],
|
||||
|
||||
'dynamodb' => [
|
||||
'driver' => 'dynamodb',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
|
||||
'endpoint' => env('DYNAMODB_ENDPOINT'),
|
||||
],
|
||||
|
||||
'octane' => [
|
||||
'driver' => 'octane',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'stores' => [
|
||||
'database',
|
||||
'array',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
|
||||
| stores, there might be other applications using the same cache. For
|
||||
| that reason, you may prefix every cache key to avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'),
|
||||
|
||||
];
|
||||
183
config/database.php
Normal file
183
config/database.php
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for database operations. This is
|
||||
| the connection which will be utilized unless another connection
|
||||
| is explicitly specified when you execute a query / statement.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below are all of the database connections defined for your application.
|
||||
| An example configuration is provided for each database system which
|
||||
| is supported by Laravel. You're free to add / remove connections.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
'busy_timeout' => null,
|
||||
'journal_mode' => null,
|
||||
'synchronous' => null,
|
||||
'transaction_mode' => 'DEFERRED',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'mariadb' => [
|
||||
'driver' => 'mariadb',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '3306'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'unix_socket' => env('DB_SOCKET', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8mb4'),
|
||||
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
]) : [],
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', '127.0.0.1'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'search_path' => 'public',
|
||||
'sslmode' => 'prefer',
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'url' => env('DB_URL'),
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'port' => env('DB_PORT', '1433'),
|
||||
'database' => env('DB_DATABASE', 'laravel'),
|
||||
'username' => env('DB_USERNAME', 'root'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => env('DB_CHARSET', 'utf8'),
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
|
||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run on the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => [
|
||||
'table' => 'migrations',
|
||||
'update_date_on_publish' => true,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer body of commands than a typical key-value system
|
||||
| such as Memcached. You may define your connection settings here.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'),
|
||||
'persistent' => env('REDIS_PERSISTENT', false),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_DB', '0'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'url' => env('REDIS_URL'),
|
||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||
'username' => env('REDIS_USERNAME'),
|
||||
'password' => env('REDIS_PASSWORD'),
|
||||
'port' => env('REDIS_PORT', '6379'),
|
||||
'database' => env('REDIS_CACHE_DB', '1'),
|
||||
'max_retries' => env('REDIS_MAX_RETRIES', 3),
|
||||
'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
|
||||
'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
|
||||
'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
80
config/filesystems.php
Normal file
80
config/filesystems.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. The "local" disk, as well as a variety of cloud
|
||||
| based disks are available to your application for file storage.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below you may configure as many filesystem disks as necessary, and you
|
||||
| may even configure multiple disks for the same driver. Examples for
|
||||
| most supported storage drivers are configured here for reference.
|
||||
|
|
||||
| Supported drivers: "local", "ftp", "sftp", "s3"
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/private'),
|
||||
'serve' => true,
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'visibility' => 'public',
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION'),
|
||||
'bucket' => env('AWS_BUCKET'),
|
||||
'url' => env('AWS_URL'),
|
||||
'endpoint' => env('AWS_ENDPOINT'),
|
||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||
'throw' => false,
|
||||
'report' => false,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Symbolic Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the symbolic links that will be created when the
|
||||
| `storage:link` Artisan command is executed. The array keys should be
|
||||
| the locations of the links and the values should be their targets.
|
||||
|
|
||||
*/
|
||||
|
||||
'links' => [
|
||||
public_path('storage') => storage_path('app/public'),
|
||||
],
|
||||
|
||||
];
|
||||
132
config/logging.php
Normal file
132
config/logging.php
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
use Monolog\Handler\NullHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogUdpHandler;
|
||||
use Monolog\Processor\PsrLogMessageProcessor;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default log channel that is utilized to write
|
||||
| messages to your logs. The value provided here should match one of
|
||||
| the channels present in the list of "channels" configured below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'stack'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Deprecations Log Channel
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the log channel that should be used to log warnings
|
||||
| regarding deprecated PHP and library features. This allows you to get
|
||||
| your application ready for upcoming major versions of dependencies.
|
||||
|
|
||||
*/
|
||||
|
||||
'deprecations' => [
|
||||
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
|
||||
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log Channels
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log channels for your application. Laravel
|
||||
| utilizes the Monolog PHP logging library, which includes a variety
|
||||
| of powerful log handlers and formatters that you're free to use.
|
||||
|
|
||||
| Available drivers: "single", "daily", "slack", "syslog",
|
||||
| "errorlog", "monolog", "custom", "stack"
|
||||
|
|
||||
*/
|
||||
|
||||
'channels' => [
|
||||
|
||||
'stack' => [
|
||||
'driver' => 'stack',
|
||||
'channels' => explode(',', (string) env('LOG_STACK', 'single')),
|
||||
'ignore_exceptions' => false,
|
||||
],
|
||||
|
||||
'single' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'daily' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'days' => env('LOG_DAILY_DAYS', 14),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
|
||||
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
|
||||
'level' => env('LOG_LEVEL', 'critical'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'papertrail' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
|
||||
'handler_with' => [
|
||||
'host' => env('PAPERTRAIL_URL'),
|
||||
'port' => env('PAPERTRAIL_PORT'),
|
||||
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
|
||||
],
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'stderr' => [
|
||||
'driver' => 'monolog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'handler' => StreamHandler::class,
|
||||
'handler_with' => [
|
||||
'stream' => 'php://stderr',
|
||||
],
|
||||
'formatter' => env('LOG_STDERR_FORMATTER'),
|
||||
'processors' => [PsrLogMessageProcessor::class],
|
||||
],
|
||||
|
||||
'syslog' => [
|
||||
'driver' => 'syslog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'errorlog' => [
|
||||
'driver' => 'errorlog',
|
||||
'level' => env('LOG_LEVEL', 'debug'),
|
||||
'replace_placeholders' => true,
|
||||
],
|
||||
|
||||
'null' => [
|
||||
'driver' => 'monolog',
|
||||
'handler' => NullHandler::class,
|
||||
],
|
||||
|
||||
'emergency' => [
|
||||
'path' => storage_path('logs/laravel.log'),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
||||
118
config/mail.php
Normal file
118
config/mail.php
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Mailer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default mailer that is used to send all email
|
||||
| messages unless another mailer is explicitly specified when sending
|
||||
| the message. All additional mailers can be configured within the
|
||||
| "mailers" array. Examples of each type of mailer are provided.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'log'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mailer Configurations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure all of the mailers used by your application plus
|
||||
| their respective settings. Several examples have been configured for
|
||||
| you and you are free to add your own as your application requires.
|
||||
|
|
||||
| Laravel supports a variety of mail "transport" drivers that can be used
|
||||
| when delivering an email. You may specify which one you're using for
|
||||
| your mailers below. You may also add additional mailers if needed.
|
||||
|
|
||||
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
|
||||
| "postmark", "resend", "log", "array",
|
||||
| "failover", "roundrobin"
|
||||
|
|
||||
*/
|
||||
|
||||
'mailers' => [
|
||||
|
||||
'smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'scheme' => env('MAIL_SCHEME'),
|
||||
'url' => env('MAIL_URL'),
|
||||
'host' => env('MAIL_HOST', '127.0.0.1'),
|
||||
'port' => env('MAIL_PORT', 2525),
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
'timeout' => null,
|
||||
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'transport' => 'ses',
|
||||
],
|
||||
|
||||
'postmark' => [
|
||||
'transport' => 'postmark',
|
||||
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
|
||||
// 'client' => [
|
||||
// 'timeout' => 5,
|
||||
// ],
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'transport' => 'resend',
|
||||
],
|
||||
|
||||
'sendmail' => [
|
||||
'transport' => 'sendmail',
|
||||
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'transport' => 'log',
|
||||
'channel' => env('MAIL_LOG_CHANNEL'),
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'transport' => 'array',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'transport' => 'failover',
|
||||
'mailers' => [
|
||||
'smtp',
|
||||
'log',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
'roundrobin' => [
|
||||
'transport' => 'roundrobin',
|
||||
'mailers' => [
|
||||
'ses',
|
||||
'postmark',
|
||||
],
|
||||
'retry_after' => 60,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all emails sent by your application to be sent from
|
||||
| the same address. Here you may specify a name and address that is
|
||||
| used globally for all emails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
|
||||
];
|
||||
129
config/queue.php
Normal file
129
config/queue.php
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel's queue supports a variety of backends via a single, unified
|
||||
| API, giving you convenient access to each backend using identical
|
||||
| syntax for each. The default queue connection is defined below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_CONNECTION', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection options for every queue backend
|
||||
| used by your application. An example configuration is provided for
|
||||
| each backend supported by Laravel. You're also free to add more.
|
||||
|
|
||||
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis",
|
||||
| "deferred", "background", "failover", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'connection' => env('DB_QUEUE_CONNECTION'),
|
||||
'table' => env('DB_QUEUE_TABLE', 'jobs'),
|
||||
'queue' => env('DB_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
|
||||
'queue' => env('BEANSTALKD_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => 0,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
|
||||
'queue' => env('SQS_QUEUE', 'default'),
|
||||
'suffix' => env('SQS_SUFFIX'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
|
||||
'queue' => env('REDIS_QUEUE', 'default'),
|
||||
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
|
||||
'block_for' => null,
|
||||
'after_commit' => false,
|
||||
],
|
||||
|
||||
'deferred' => [
|
||||
'driver' => 'deferred',
|
||||
],
|
||||
|
||||
'background' => [
|
||||
'driver' => 'background',
|
||||
],
|
||||
|
||||
'failover' => [
|
||||
'driver' => 'failover',
|
||||
'connections' => [
|
||||
'database',
|
||||
'deferred',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Batching
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following options configure the database and table that store job
|
||||
| batching information. These options can be updated to any database
|
||||
| connection and table which has been defined by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'batching' => [
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'job_batches',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control how and where failed jobs are stored. Laravel ships with
|
||||
| support for storing failed jobs in a simple file or in a database.
|
||||
|
|
||||
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'sqlite'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
||||
38
config/services.php
Normal file
38
config/services.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Mailgun, Postmark, AWS and more. This file provides the de facto
|
||||
| location for this type of information, allowing packages to have
|
||||
| a conventional file to locate the various service credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'postmark' => [
|
||||
'key' => env('POSTMARK_API_KEY'),
|
||||
],
|
||||
|
||||
'resend' => [
|
||||
'key' => env('RESEND_API_KEY'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||
'secret' => env('AWS_SECRET_ACCESS_KEY'),
|
||||
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'notifications' => [
|
||||
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
|
||||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
217
config/session.php
Normal file
217
config/session.php
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines the default session driver that is utilized for
|
||||
| incoming requests. Laravel supports a variety of storage options to
|
||||
| persist session data. Database storage is a great default choice.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "memcached",
|
||||
| "redis", "dynamodb", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'database'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to expire immediately when the browser is closed then you may
|
||||
| indicate that via the expire_on_close configuration option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => (int) env('SESSION_LIFETIME', 120),
|
||||
|
||||
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it's stored. All encryption is performed
|
||||
| automatically by Laravel and you may use the session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => env('SESSION_ENCRYPT', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing the "file" session driver, the session files are placed
|
||||
| on disk. The default storage location is defined here; however, you
|
||||
| are free to provide another location where they should be stored.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => env('SESSION_CONNECTION'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table to
|
||||
| be used to store sessions. Of course, a sensible default is defined
|
||||
| for you; however, you're welcome to change this to another table.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => env('SESSION_TABLE', 'sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using one of the framework's cache driven session backends, you may
|
||||
| define the cache store which should be used to store the session data
|
||||
| between requests. This must match one of your defined cache stores.
|
||||
|
|
||||
| Affects: "dynamodb", "memcached", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'store' => env('SESSION_STORE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the session cookie that is created by
|
||||
| the framework. Typically, you should not need to change this value
|
||||
| since doing so does not grant a meaningful security improvement.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug((string) env('APP_NAME', 'laravel')).'-session'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application, but you're free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => env('SESSION_PATH', '/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the domain and subdomains the session cookie is
|
||||
| available to. By default, the cookie will be available to the root
|
||||
| domain without subdomains. Typically, this shouldn't be changed.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => env('SESSION_DOMAIN'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you when it can't be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => env('SESSION_SECURE_COOKIE'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTP Access Only
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will prevent JavaScript from accessing the
|
||||
| value of the cookie and the cookie will only be accessible through
|
||||
| the HTTP protocol. It's unlikely you should disable this option.
|
||||
|
|
||||
*/
|
||||
|
||||
'http_only' => env('SESSION_HTTP_ONLY', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Same-Site Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines how your cookies behave when cross-site requests
|
||||
| take place, and can be used to mitigate CSRF attacks. By default, we
|
||||
| will set this value to "lax" to permit secure cross-site requests.
|
||||
|
|
||||
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
||||
|
|
||||
| Supported: "lax", "strict", "none", null
|
||||
|
|
||||
*/
|
||||
|
||||
'same_site' => env('SESSION_SAME_SITE', 'lax'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Partitioned Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Setting this value to true will tie the cookie to the top-level site for
|
||||
| a cross-site context. Partitioned cookies are accepted by the browser
|
||||
| when flagged "secure" and the Same-Site attribute is set to "none".
|
||||
|
|
||||
*/
|
||||
|
||||
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
|
||||
|
||||
];
|
||||
1
database/.gitignore
vendored
Normal file
1
database/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.sqlite*
|
||||
44
database/factories/UserFactory.php
Normal file
44
database/factories/UserFactory.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
49
database/migrations/0001_01_01_000000_create_users_table.php
Normal file
49
database/migrations/0001_01_01_000000_create_users_table.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
35
database/migrations/0001_01_01_000001_create_cache_table.php
Normal file
35
database/migrations/0001_01_01_000001_create_cache_table.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue