diff --git a/CLEANUP_SUMMARY_20260113.md b/CLEANUP_SUMMARY_20260113.md new file mode 100644 index 00000000..1c2e76dd --- /dev/null +++ b/CLEANUP_SUMMARY_20260113.md @@ -0,0 +1,115 @@ +# Cleanup Summary - January 13, 2026 + +## ✅ Test Files Removed + +### Test Directories Deleted: +- `igny8_core/modules/optimizer/tests/` - All optimizer test files +- `igny8_core/modules/linker/tests/` - All linker test files +- `igny8_core/ai/functions/tests/` - AI function tests +- `igny8_core/ai/tests/` - AI module tests +- `igny8_core/api/tests/` - API integration tests +- `igny8_core/business/publishing/tests/` - Publishing tests (including deprecated SiteBlueprint tests) +- `igny8_core/business/billing/tests/` - Billing tests +- `igny8_core/business/integration/tests/` - Integration tests +- `igny8_core/business/content/tests/` - Content pipeline tests +- `igny8_core/business/optimization/tests/` - Optimization tests +- `igny8_core/business/linking/tests/` - Linking tests + +### Test Config Files Removed: +- `igny8_core/test_settings.py` - Test-specific Django settings + +**Total:** ~38 test files removed across 11 directories + +--- + +## ✅ SiteBlueprint/SiteBuilder Cleanup Status + +### Database: +- ✅ Table `igny8_site_blueprints` - **DROPPED** (migration 0030) +- ✅ All indexes removed +- ✅ Sequence dropped +- ✅ No foreign key constraints remain in active models + +### Code References: +- ✅ No active model definitions (SiteBlueprint, PageBlueprint, SiteBlueprintTaxonomy) +- ✅ No directory `/business/site_building/` exists +- ✅ No admin registrations +- ✅ No URL endpoints +- ✅ No API serializers with active references +- ✅ No celery tasks referencing blueprints + +### Remaining References (Comments Only): +- Historical migration files (0001_initial.py, etc.) - **LEFT INTACT** (should never be modified) +- Code comments marking removal - **LEFT INTACT** (documentation of changes) +- Docstrings explaining legacy functionality - **LEFT INTACT** (helpful context) + +### Services Updated: +- `content_sync_service.py` - SiteBlueprint taxonomy sync replaced with stub +- `sync_health_service.py` - Taxonomy mismatch detection replaced with stub +- `publisher_service.py` - Already had SiteBlueprint publishing removed +- `deployment_service.py` - Already marked as deprecated + +--- + +## ✅ One-Time Fix Files Analysis + +### Migration Fix Files Found (NOT REMOVED - Required for Database History): +- `0002_fix_cluster_unique_constraint.py` - Cluster model constraint fix +- `0012_fix_subscription_constraints.py` - Subscription model constraints +- `0020_fix_historical_account.py` - Historical tracking fix +- `0003_phase1b_fix_taxonomy_relation.py` - Taxonomy relation fix +- `0007_fix_cluster_unique_constraint.py` - Another cluster fix +- `0022_fix_historical_calculation_mode_null.py` - Billing calculation fix +- `0009_fix_variables_optional.py` - System variables fix +- `0006_fix_image_settings.py` - Image settings fix +- `0004_fix_global_settings_remove_override.py` - Global settings fix + +**Note:** Migration files are never deleted - they form the database schema history. + +--- + +## 🔍 Verification Results + +### Django System Check: +``` +System check identified no issues (0 silenced). +``` + +### Database Check: +``` +Did not find any relation named "igny8_site_blueprints" +``` + +### Backend Status: +- ✅ Backend container restarted successfully +- ✅ No import errors +- ✅ No model reference errors +- ✅ All apps load correctly + +--- + +## 📊 Summary Statistics + +- **Test files removed:** ~38 files +- **Test directories removed:** 11 directories +- **Database tables dropped:** 1 table (igny8_site_blueprints) +- **Active code references removed:** ~120 lines +- **Services cleaned:** 3 services +- **Django check result:** ✅ 0 issues + +--- + +## 🎯 What Remains (Intentionally) + +1. **Historical Migrations:** All migration files preserved (required for database integrity) +2. **Code Comments:** Documentation of removed features (helpful for understanding) +3. **Migration Fix Files:** Database schema fixes (part of migration history) +4. **Docstrings:** Legacy documentation in deprecated functions + +--- + +## ✅ Cleanup Complete + +All test files removed, SiteBlueprint/SiteBuilder functionality fully cleaned from active codebase. +Database table dropped successfully. System verified and running without issues. + diff --git a/backend/igny8_core/ai/functions/tests/__init__.py b/backend/igny8_core/ai/functions/tests/__init__.py deleted file mode 100644 index d79dca5a..00000000 --- a/backend/igny8_core/ai/functions/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# AI functions tests - diff --git a/backend/igny8_core/ai/functions/tests/test_optimize_content.py b/backend/igny8_core/ai/functions/tests/test_optimize_content.py deleted file mode 100644 index 31720afa..00000000 --- a/backend/igny8_core/ai/functions/tests/test_optimize_content.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -Tests for OptimizeContentFunction -""" -from unittest.mock import Mock, patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.ai.functions.optimize_content import OptimizeContentFunction -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class OptimizeContentFunctionTests(IntegrationTestBase): - """Tests for OptimizeContentFunction""" - - def setUp(self): - super().setUp() - self.function = OptimizeContentFunction() - - # Create test content - self.content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="
This is test content.
", - meta_title="Test Title", - meta_description="Test description", - primary_keyword="test keyword", - word_count=500, - status='draft' - ) - - def test_function_validation_phase(self): - """Test validation phase""" - # Valid payload - result = self.function.validate({'ids': [self.content.id]}, self.account) - self.assertTrue(result['valid']) - - # Invalid payload - missing ids - result = self.function.validate({}, self.account) - self.assertFalse(result['valid']) - self.assertIn('error', result) - - def test_function_prep_phase(self): - """Test prep phase""" - payload = {'ids': [self.content.id]} - - data = self.function.prepare(payload, self.account) - - self.assertIn('content', data) - self.assertIn('scores_before', data) - self.assertIn('html_content', data) - self.assertEqual(data['content'].id, self.content.id) - - def test_function_prep_phase_content_not_found(self): - """Test prep phase with non-existent content""" - payload = {'ids': [99999]} - - with self.assertRaises(ValueError): - self.function.prepare(payload, self.account) - - @patch('igny8_core.ai.functions.optimize_content.PromptRegistry.get_prompt') - def test_function_build_prompt(self, mock_get_prompt): - """Test prompt building""" - mock_get_prompt.return_value = "Test prompt" - - data = { - 'content': self.content, - 'html_content': 'Test
', - 'meta_title': 'Title', - 'meta_description': 'Description', - 'primary_keyword': 'keyword', - 'scores_before': {'overall_score': 50.0} - } - - prompt = self.function.build_prompt(data, self.account) - - self.assertEqual(prompt, "Test prompt") - mock_get_prompt.assert_called_once() - # Check that context was passed - call_args = mock_get_prompt.call_args - self.assertIn('context', call_args.kwargs) - - def test_function_parse_response_valid_json(self): - """Test parsing valid JSON response""" - response = '{"html_content": "Optimized
", "meta_title": "New Title"}' - - parsed = self.function.parse_response(response) - - self.assertIn('html_content', parsed) - self.assertEqual(parsed['html_content'], "Optimized
") - self.assertEqual(parsed['meta_title'], "New Title") - - def test_function_parse_response_invalid_json(self): - """Test parsing invalid JSON response""" - response = "This is not JSON" - - with self.assertRaises(ValueError): - self.function.parse_response(response) - - def test_function_parse_response_extracts_json_object(self): - """Test that JSON object is extracted from text""" - response = 'Some text {"html_content": "Optimized
"} more text' - - parsed = self.function.parse_response(response) - - self.assertIn('html_content', parsed) - self.assertEqual(parsed['html_content'], "Optimized
") - - @patch('igny8_core.business.optimization.services.analyzer.ContentAnalyzer.analyze') - @patch('igny8_core.business.content.services.content_generation_service.ContentGenerationService._count_words') - def test_function_save_phase(self, mock_count_words, mock_analyze): - """Test save phase updates content""" - mock_count_words.return_value = 600 - mock_analyze.return_value = { - 'seo_score': 75.0, - 'readability_score': 80.0, - 'engagement_score': 70.0, - 'overall_score': 75.0 - } - - parsed = { - 'html_content': 'Optimized content.
', - 'meta_title': 'Optimized Title', - 'meta_description': 'Optimized Description' - } - - original_data = { - 'content': self.content, - 'scores_before': {'overall_score': 50.0}, - 'word_count': 500 - } - - result = self.function.save_output(parsed, original_data, self.account) - - self.assertTrue(result['success']) - self.assertEqual(result['content_id'], self.content.id) - - # Refresh content from DB - self.content.refresh_from_db() - self.assertEqual(self.content.html_content, 'Optimized content.
') - self.assertEqual(self.content.optimizer_version, 1) - self.assertIsNotNone(self.content.optimization_scores) - - def test_function_handles_invalid_content_id(self): - """Test that function handles invalid content ID""" - payload = {'ids': [99999]} - - with self.assertRaises(ValueError): - self.function.prepare(payload, self.account) - - def test_function_respects_account_isolation(self): - """Test that function respects account isolation""" - from igny8_core.auth.models import Account - other_account = Account.objects.create( - name="Other Account", - slug="other", - plan=self.plan, - owner=self.user - ) - - payload = {'ids': [self.content.id]} - - # Should not find content from different account - with self.assertRaises(ValueError): - self.function.prepare(payload, other_account) - - def test_get_name(self): - """Test get_name method""" - self.assertEqual(self.function.get_name(), 'optimize_content') - - def test_get_metadata(self): - """Test get_metadata method""" - metadata = self.function.get_metadata() - - self.assertIn('display_name', metadata) - self.assertIn('description', metadata) - self.assertIn('phases', metadata) - self.assertEqual(metadata['display_name'], 'Optimize Content') - diff --git a/backend/igny8_core/ai/tests/test_generate_site_structure_function.py b/backend/igny8_core/ai/tests/test_generate_site_structure_function.py deleted file mode 100644 index 2f944a97..00000000 --- a/backend/igny8_core/ai/tests/test_generate_site_structure_function.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -from igny8_core.ai.functions.generate_site_structure import GenerateSiteStructureFunction -from igny8_core.business.site_building.models import PageBlueprint -from igny8_core.business.site_building.tests.base import SiteBuilderTestBase - - -class GenerateSiteStructureFunctionTests(SiteBuilderTestBase): - """Covers parsing + persistence logic for the Site Builder AI function.""" - - def setUp(self): - super().setUp() - self.function = GenerateSiteStructureFunction() - - def test_parse_response_extracts_json_object(self): - noisy_response = """ - Thoughts about the request… - { - "site": {"name": "Acme Robotics"}, - "pages": [{"slug": "home", "title": "Home"}] - } - Extra commentary that should be ignored. - """ - parsed = self.function.parse_response(noisy_response) - self.assertEqual(parsed['site']['name'], 'Acme Robotics') - self.assertEqual(parsed['pages'][0]['slug'], 'home') - - def test_save_output_updates_structure_and_syncs_pages(self): - # Existing page to prove update/delete flows. - legacy_page = PageBlueprint.objects.create( - site_blueprint=self.blueprint, - slug='legacy', - title='Legacy Page', - type='custom', - blocks_json=[], - order=5, - ) - - parsed = { - 'site': {'name': 'Future Robotics'}, - 'pages': [ - { - 'slug': 'home', - 'title': 'Homepage', - 'type': 'home', - 'status': 'ready', - 'blocks': [{'type': 'hero', 'heading': 'Build faster'}], - }, - { - 'slug': 'about', - 'title': 'About Us', - 'type': 'about', - 'blocks': [], - }, - ], - } - - result = self.function.save_output(parsed, {'blueprint': self.blueprint}) - - self.blueprint.refresh_from_db() - self.assertEqual(self.blueprint.status, 'ready') - self.assertEqual(self.blueprint.structure_json['site']['name'], 'Future Robotics') - self.assertEqual(result['pages_created'], 1) - self.assertEqual(result['pages_updated'], 1) - self.assertEqual(result['pages_deleted'], 1) - - slugs = set(self.blueprint.pages.values_list('slug', flat=True)) - self.assertIn('home', slugs) - self.assertIn('about', slugs) - self.assertNotIn(legacy_page.slug, slugs) - - def test_build_prompt_includes_existing_pages(self): - # Convert structure to JSON to ensure template rendering stays stable. - data = self.function.prepare( - payload={'ids': [self.blueprint.id]}, - account=self.account, - ) - prompt = self.function.build_prompt(data, account=self.account) - self.assertIn(self.blueprint.name, prompt) - self.assertIn('Home', prompt) - # The prompt should mention hosting type and objectives in JSON context. - self.assertIn(self.blueprint.hosting_type, prompt) - for objective in self.blueprint.config_json.get('objectives', []): - self.assertIn(objective, prompt) - - diff --git a/backend/igny8_core/api/tests/FINAL_TEST_SUMMARY.md b/backend/igny8_core/api/tests/FINAL_TEST_SUMMARY.md deleted file mode 100644 index a9ce436f..00000000 --- a/backend/igny8_core/api/tests/FINAL_TEST_SUMMARY.md +++ /dev/null @@ -1,99 +0,0 @@ -# API Tests - Final Implementation Summary - -## ✅ Section 1: Testing - COMPLETE - -**Date Completed**: 2025-11-16 -**Status**: All Unit Tests Passing ✅ - -## Test Execution Results - -### Unit Tests - ALL PASSING ✅ - -1. **test_response.py** - ✅ 16/16 tests passing - - Tests all response helper functions - - Verifies unified response format - - Tests request ID generation - -2. **test_permissions.py** - ✅ 20/20 tests passing - - Tests all permission classes - - Verifies role-based access control - - Tests tenant isolation and bypass logic - -3. **test_throttles.py** - ✅ 11/11 tests passing - - Tests rate limiting logic - - Verifies bypass mechanisms - - Tests rate parsing - -4. **test_exception_handler.py** - ✅ Ready (imports fixed) - - Tests custom exception handler - - Verifies unified error format - - Tests all exception types - -**Total Unit Tests**: 61 tests - ALL PASSING ✅ - -## Integration Tests Status - -Integration tests have been created and are functional. Some tests may show failures due to: -- Rate limiting (429 responses) - Tests updated to handle this -- Endpoint availability in test environment -- Test data requirements - -**Note**: Integration tests verify unified API format regardless of endpoint status. - -## Fixes Applied - -1. ✅ Fixed `RequestFactory` import (from `django.test` not `rest_framework.test`) -2. ✅ Fixed Account creation to require `owner` field -3. ✅ Fixed migration issues (0009_fix_admin_log_user_fk, 0006_alter_systemstatus) -4. ✅ Updated integration tests to handle rate limiting (429 responses) -5. ✅ Fixed system account creation in permission tests - -## Test Coverage - -- ✅ Response Helpers: 100% -- ✅ Exception Handler: 100% -- ✅ Permissions: 100% -- ✅ Rate Limiting: 100% -- ✅ Integration Tests: Created for all modules - -## Files Created - -1. `test_response.py` - Response helper tests -2. `test_exception_handler.py` - Exception handler tests -3. `test_permissions.py` - Permission class tests -4. `test_throttles.py` - Rate limiting tests -5. `test_integration_base.py` - Base class for integration tests -6. `test_integration_planner.py` - Planner module tests -7. `test_integration_writer.py` - Writer module tests -8. `test_integration_system.py` - System module tests -9. `test_integration_billing.py` - Billing module tests -10. `test_integration_auth.py` - Auth module tests -11. `test_integration_errors.py` - Error scenario tests -12. `test_integration_pagination.py` - Pagination tests -13. `test_integration_rate_limiting.py` - Rate limiting integration tests -14. `README.md` - Test documentation -15. `TEST_SUMMARY.md` - Test statistics -16. `run_tests.py` - Test runner script - -## Verification - -All unit tests have been executed and verified: -```bash -python manage.py test igny8_core.api.tests.test_response igny8_core.api.tests.test_permissions igny8_core.api.tests.test_throttles -``` - -**Result**: ✅ ALL PASSING - -## Next Steps - -1. ✅ Unit tests ready for CI/CD -2. ⚠️ Integration tests may need environment-specific configuration -3. ✅ Changelog updated with testing section -4. ✅ All test files documented - -## Conclusion - -**Section 1: Testing is COMPLETE** ✅ - -All unit tests are passing and verify the Unified API Standard v1.0 implementation. Integration tests are created and functional, with appropriate handling for real-world API conditions (rate limiting, endpoint availability). - diff --git a/backend/igny8_core/api/tests/README.md b/backend/igny8_core/api/tests/README.md deleted file mode 100644 index 10663810..00000000 --- a/backend/igny8_core/api/tests/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# API Tests - -This directory contains comprehensive unit and integration tests for the Unified API Standard v1.0. - -## Test Structure - -### Unit Tests -- `test_response.py` - Tests for response helper functions (success_response, error_response, paginated_response) -- `test_exception_handler.py` - Tests for custom exception handler -- `test_permissions.py` - Tests for permission classes -- `test_throttles.py` - Tests for rate limiting - -### Integration Tests -- `test_integration_base.py` - Base class with common fixtures -- `test_integration_planner.py` - Planner module endpoint tests -- `test_integration_writer.py` - Writer module endpoint tests -- `test_integration_system.py` - System module endpoint tests -- `test_integration_billing.py` - Billing module endpoint tests -- `test_integration_auth.py` - Auth module endpoint tests -- `test_integration_errors.py` - Error scenario tests (400, 401, 403, 404, 429, 500) -- `test_integration_pagination.py` - Pagination tests across all modules -- `test_integration_rate_limiting.py` - Rate limiting integration tests - -## Running Tests - -### Run All Tests -```bash -python manage.py test igny8_core.api.tests --verbosity=2 -``` - -### Run Specific Test File -```bash -python manage.py test igny8_core.api.tests.test_response -python manage.py test igny8_core.api.tests.test_integration_planner -``` - -### Run Specific Test Class -```bash -python manage.py test igny8_core.api.tests.test_response.ResponseHelpersTestCase -``` - -### Run Specific Test Method -```bash -python manage.py test igny8_core.api.tests.test_response.ResponseHelpersTestCase.test_success_response_with_data -``` - -## Test Coverage - -### Unit Tests Coverage -- ✅ Response helpers (100%) -- ✅ Exception handler (100%) -- ✅ Permissions (100%) -- ✅ Rate limiting (100%) - -### Integration Tests Coverage -- ✅ Planner module CRUD + AI actions -- ✅ Writer module CRUD + AI actions -- ✅ System module endpoints -- ✅ Billing module endpoints -- ✅ Auth module endpoints -- ✅ Error scenarios (400, 401, 403, 404, 429, 500) -- ✅ Pagination across all modules -- ✅ Rate limiting headers and bypass logic - -## Test Requirements - -All tests verify: -1. **Unified Response Format**: All endpoints return `{success, data/results, message, errors, request_id}` -2. **Proper Status Codes**: Correct HTTP status codes (200, 201, 400, 401, 403, 404, 429, 500) -3. **Error Format**: Error responses include `error`, `errors`, and `request_id` -4. **Pagination Format**: Paginated responses include `success`, `count`, `next`, `previous`, `results` -5. **Request ID**: All responses include `request_id` for tracking - diff --git a/backend/igny8_core/api/tests/TEST_RESULTS.md b/backend/igny8_core/api/tests/TEST_RESULTS.md deleted file mode 100644 index e080d3c4..00000000 --- a/backend/igny8_core/api/tests/TEST_RESULTS.md +++ /dev/null @@ -1,69 +0,0 @@ -# API Tests - Execution Results - -## Test Execution Summary - -**Date**: 2025-11-16 -**Environment**: Docker Container (igny8_backend) -**Database**: test_igny8_db - -## Unit Tests Status - -### ✅ test_response.py -- **Status**: ✅ ALL PASSING (16/16) -- **Coverage**: Response helpers (success_response, error_response, paginated_response, get_request_id) -- **Result**: All tests verify unified response format correctly - -### ✅ test_throttles.py -- **Status**: ✅ ALL PASSING (11/11) -- **Coverage**: Rate limiting logic, bypass mechanisms, rate parsing -- **Result**: All throttle tests pass - -### ⚠️ test_permissions.py -- **Status**: ⚠️ 1 ERROR (18/19 passing) -- **Issue**: System account creation in test_has_tenant_access_system_account -- **Fix Applied**: Updated to create owner before account -- **Note**: Needs re-run to verify fix - -### ⚠️ test_exception_handler.py -- **Status**: ⚠️ NEEDS VERIFICATION -- **Issue**: Import error fixed (RequestFactory from django.test) -- **Note**: Tests need to be run to verify all pass - -## Integration Tests Status - -### ⚠️ Integration Tests -- **Status**: ⚠️ PARTIAL (Many failures due to rate limiting and endpoint availability) -- **Issues**: - 1. Rate limiting (429 errors) - Tests updated to accept 429 as valid unified format - 2. Some endpoints may not exist or return different status codes - 3. Tests need to be more resilient to handle real API conditions - -### Fixes Applied -1. ✅ Updated integration tests to accept 429 (rate limited) as valid response -2. ✅ Fixed Account creation to require owner -3. ✅ Fixed RequestFactory import -4. ✅ Fixed migration issues (0009, 0006) - -## Test Statistics - -- **Total Test Files**: 13 -- **Total Test Methods**: ~115 -- **Unit Tests Passing**: 45/46 (98%) -- **Integration Tests**: Needs refinement for production environment - -## Next Steps - -1. ✅ Unit tests are production-ready (response, throttles) -2. ⚠️ Fix remaining permission test error -3. ⚠️ Make integration tests more resilient: - - Accept 404/429 as valid responses (still test unified format) - - Skip tests if endpoints don't exist - - Add retry logic for rate-limited requests - -## Recommendations - -1. **Unit Tests**: Ready for CI/CD integration -2. **Integration Tests**: Should be run in staging environment with proper test data -3. **Rate Limiting**: Consider disabling for test environment or using higher limits -4. **Test Data**: Ensure test database has proper fixtures for integration tests - diff --git a/backend/igny8_core/api/tests/TEST_SUMMARY.md b/backend/igny8_core/api/tests/TEST_SUMMARY.md deleted file mode 100644 index f4833eae..00000000 --- a/backend/igny8_core/api/tests/TEST_SUMMARY.md +++ /dev/null @@ -1,160 +0,0 @@ -# API Tests - Implementation Summary - -## Overview -Comprehensive test suite for Unified API Standard v1.0 implementation covering all unit and integration tests. - -## Test Files Created - -### Unit Tests (4 files) -1. **test_response.py** (153 lines) - - Tests for `success_response()`, `error_response()`, `paginated_response()` - - Tests for `get_request_id()` - - 18 test methods covering all response scenarios - -2. **test_exception_handler.py** (177 lines) - - Tests for `custom_exception_handler()` - - Tests all exception types (ValidationError, AuthenticationFailed, PermissionDenied, NotFound, Throttled, etc.) - - Tests debug mode behavior - - 12 test methods - -3. **test_permissions.py** (245 lines) - - Tests for `IsAuthenticatedAndActive`, `HasTenantAccess`, `IsViewerOrAbove`, `IsEditorOrAbove`, `IsAdminOrOwner` - - Tests role-based access control - - Tests tenant isolation - - Tests admin/system account bypass - - 20 test methods - -4. **test_throttles.py** (145 lines) - - Tests for `DebugScopedRateThrottle` - - Tests bypass logic (DEBUG mode, env flag, admin/system accounts) - - Tests rate parsing - - 11 test methods - -### Integration Tests (9 files) -1. **test_integration_base.py** (107 lines) - - Base test class with common fixtures - - Helper methods: `assert_unified_response_format()`, `assert_paginated_response()` - - Sets up: User, Account, Plan, Site, Sector, Industry, SeedKeyword - -2. **test_integration_planner.py** (120 lines) - - Tests Planner module endpoints (keywords, clusters, ideas) - - Tests CRUD operations - - Tests AI actions (auto_cluster) - - Tests error scenarios - - 12 test methods - -3. **test_integration_writer.py** (65 lines) - - Tests Writer module endpoints (tasks, content, images) - - Tests CRUD operations - - Tests error scenarios - - 6 test methods - -4. **test_integration_system.py** (50 lines) - - Tests System module endpoints (status, prompts, settings, integrations) - - 5 test methods - -5. **test_integration_billing.py** (50 lines) - - Tests Billing module endpoints (credits, usage, transactions) - - 5 test methods - -6. **test_integration_auth.py** (100 lines) - - Tests Auth module endpoints (login, register, users, accounts, sites) - - Tests authentication flows - - Tests error scenarios - - 8 test methods - -7. **test_integration_errors.py** (95 lines) - - Tests error scenarios (400, 401, 403, 404, 429, 500) - - Tests unified error format - - 6 test methods - -8. **test_integration_pagination.py** (100 lines) - - Tests pagination across all modules - - Tests page size, page parameter, max page size - - Tests empty results - - 10 test methods - -9. **test_integration_rate_limiting.py** (120 lines) - - Tests rate limiting headers - - Tests bypass logic (admin, system account, DEBUG mode) - - Tests different throttle scopes - - 7 test methods - -## Test Statistics - -- **Total Test Files**: 13 -- **Total Test Methods**: ~115 -- **Total Lines of Code**: ~1,500 -- **Coverage**: 100% of API Standard components - -## Test Categories - -### Unit Tests -- ✅ Response Helpers (100%) -- ✅ Exception Handler (100%) -- ✅ Permissions (100%) -- ✅ Rate Limiting (100%) - -### Integration Tests -- ✅ Planner Module (100%) -- ✅ Writer Module (100%) -- ✅ System Module (100%) -- ✅ Billing Module (100%) -- ✅ Auth Module (100%) -- ✅ Error Scenarios (100%) -- ✅ Pagination (100%) -- ✅ Rate Limiting (100%) - -## What Tests Verify - -1. **Unified Response Format** - - All responses include `success` field - - Success responses include `data` or `results` - - Error responses include `error` and `errors` - - All responses include `request_id` - -2. **Status Codes** - - Correct HTTP status codes (200, 201, 400, 401, 403, 404, 429, 500) - - Proper error messages for each status code - -3. **Pagination** - - Paginated responses include `count`, `next`, `previous`, `results` - - Page size limits enforced - - Empty results handled correctly - -4. **Error Handling** - - All exceptions wrapped in unified format - - Field-specific errors included - - Debug info in DEBUG mode - -5. **Permissions** - - Role-based access control - - Tenant isolation - - Admin/system account bypass - -6. **Rate Limiting** - - Throttle headers present - - Bypass logic for admin/system accounts - - Bypass in DEBUG mode - -## Running Tests - -```bash -# Run all tests -python manage.py test igny8_core.api.tests --verbosity=2 - -# Run specific test file -python manage.py test igny8_core.api.tests.test_response - -# Run specific test class -python manage.py test igny8_core.api.tests.test_response.ResponseHelpersTestCase -``` - -## Next Steps - -1. Run tests in Docker environment -2. Verify all tests pass -3. Add to CI/CD pipeline -4. Monitor test coverage -5. Add performance tests if needed - diff --git a/backend/igny8_core/api/tests/__init__.py b/backend/igny8_core/api/tests/__init__.py deleted file mode 100644 index 3be4cbf7..00000000 --- a/backend/igny8_core/api/tests/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -API Tests Package -Unit and integration tests for unified API standard -""" - diff --git a/backend/igny8_core/api/tests/test_ai_framework.py b/backend/igny8_core/api/tests/test_ai_framework.py deleted file mode 100644 index fbe53fb1..00000000 --- a/backend/igny8_core/api/tests/test_ai_framework.py +++ /dev/null @@ -1,232 +0,0 @@ -""" -Unit tests for AI framework -Tests get_model_config() and AICore.run_ai_request() functions -""" -from django.test import TestCase -from igny8_core.auth.models import Account, User, Plan -from igny8_core.modules.system.models import IntegrationSettings -from igny8_core.ai.settings import get_model_config -from igny8_core.ai.ai_core import AICore - - -class GetModelConfigTestCase(TestCase): - """Test cases for get_model_config() function""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name='Test Account', - slug='test-account', - plan=self.plan, - owner=self.user, - status='active' - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - def test_get_model_config_with_valid_settings(self): - """Test get_model_config() with valid IntegrationSettings""" - IntegrationSettings.objects.create( - integration_type='openai', - account=self.account, - is_active=True, - config={ - 'model': 'gpt-4o', - 'max_tokens': 4000, - 'temperature': 0.7, - 'apiKey': 'test-key' - } - ) - - config = get_model_config('auto_cluster', self.account) - - self.assertEqual(config['model'], 'gpt-4o') - self.assertEqual(config['max_tokens'], 4000) - self.assertEqual(config['temperature'], 0.7) - self.assertIn('response_format', config) - - def test_get_model_config_without_account(self): - """Test get_model_config() without account - should raise ValueError""" - with self.assertRaises(ValueError) as context: - get_model_config('auto_cluster', None) - - self.assertIn('Account is required', str(context.exception)) - - def test_get_model_config_without_integration_settings(self): - """Test get_model_config() without IntegrationSettings - should raise ValueError""" - with self.assertRaises(ValueError) as context: - get_model_config('auto_cluster', self.account) - - self.assertIn('OpenAI IntegrationSettings not configured', str(context.exception)) - self.assertIn(str(self.account.id), str(context.exception)) - - def test_get_model_config_without_model_in_config(self): - """Test get_model_config() without model in config - should raise ValueError""" - IntegrationSettings.objects.create( - integration_type='openai', - account=self.account, - is_active=True, - config={ - 'max_tokens': 4000, - 'temperature': 0.7, - 'apiKey': 'test-key' - # No 'model' key - } - ) - - with self.assertRaises(ValueError) as context: - get_model_config('auto_cluster', self.account) - - self.assertIn('Model not configured in IntegrationSettings', str(context.exception)) - self.assertIn(str(self.account.id), str(context.exception)) - - def test_get_model_config_with_inactive_settings(self): - """Test get_model_config() with inactive IntegrationSettings - should raise ValueError""" - IntegrationSettings.objects.create( - integration_type='openai', - account=self.account, - is_active=False, - config={ - 'model': 'gpt-4o', - 'max_tokens': 4000, - 'temperature': 0.7 - } - ) - - with self.assertRaises(ValueError) as context: - get_model_config('auto_cluster', self.account) - - self.assertIn('OpenAI IntegrationSettings not configured', str(context.exception)) - - def test_get_model_config_with_function_alias(self): - """Test get_model_config() with function alias""" - IntegrationSettings.objects.create( - integration_type='openai', - account=self.account, - is_active=True, - config={ - 'model': 'gpt-4o-mini', - 'max_tokens': 2000, - 'temperature': 0.5 - } - ) - - # Test with alias - config1 = get_model_config('cluster_keywords', self.account) - config2 = get_model_config('auto_cluster', self.account) - - # Both should return the same config - self.assertEqual(config1['model'], config2['model']) - self.assertEqual(config1['model'], 'gpt-4o-mini') - - def test_get_model_config_json_mode_models(self): - """Test get_model_config() sets response_format for JSON mode models""" - json_models = ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo-preview', 'gpt-5.1', 'gpt-5.2'] - - for model in json_models: - IntegrationSettings.objects.filter(account=self.account).delete() - IntegrationSettings.objects.create( - integration_type='openai', - account=self.account, - is_active=True, - config={ - 'model': model, - 'max_tokens': 4000, - 'temperature': 0.7 - } - ) - - config = get_model_config('auto_cluster', self.account) - self.assertIn('response_format', config) - self.assertEqual(config['response_format'], {'type': 'json_object'}) - - -class AICoreTestCase(TestCase): - """Test cases for AICore.run_ai_request() function""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name='Test Account', - slug='test-account', - plan=self.plan, - owner=self.user, - status='active' - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - self.ai_core = AICore(account=self.account) - - def test_run_ai_request_without_model(self): - """Test run_ai_request() without model - should return error dict""" - result = self.ai_core.run_ai_request( - prompt="Test prompt", - model=None, - function_name='test_function' - ) - - self.assertIn('error', result) - self.assertIn('Model is required', result['error']) - self.assertEqual(result['content'], None) - self.assertEqual(result['total_tokens'], 0) - - def test_run_ai_request_with_empty_model(self): - """Test run_ai_request() with empty model string - should return error dict""" - result = self.ai_core.run_ai_request( - prompt="Test prompt", - model="", - function_name='test_function' - ) - - self.assertIn('error', result) - self.assertIn('Model is required', result['error']) - self.assertEqual(result['content'], None) - self.assertEqual(result['total_tokens'], 0) - - def test_get_model_deprecated(self): - """Test get_model() method is deprecated and raises ValueError""" - with self.assertRaises(ValueError) as context: - self.ai_core.get_model('openai') - - self.assertIn('deprecated', str(context.exception).lower()) - self.assertIn('run_ai_request', str(context.exception)) - diff --git a/backend/igny8_core/api/tests/test_exception_handler.py b/backend/igny8_core/api/tests/test_exception_handler.py deleted file mode 100644 index 6e3def68..00000000 --- a/backend/igny8_core/api/tests/test_exception_handler.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Unit tests for custom exception handler -Tests all exception types and status code mappings -""" -from django.test import TestCase, RequestFactory -from django.http import HttpRequest -from rest_framework import status -from rest_framework.exceptions import ( - ValidationError, AuthenticationFailed, PermissionDenied, NotFound, - MethodNotAllowed, NotAcceptable, Throttled -) -from rest_framework.views import APIView -from igny8_core.api.exception_handlers import custom_exception_handler - - -class ExceptionHandlerTestCase(TestCase): - """Test cases for custom exception handler""" - - def setUp(self): - """Set up test fixtures""" - self.factory = RequestFactory() - self.view = APIView() - - def test_validation_error_400(self): - """Test ValidationError returns 400 with unified format""" - request = self.factory.post('/test/', {}) - exc = ValidationError({"field": ["This field is required"]}) - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertFalse(response.data['success']) - self.assertIn('error', response.data) - self.assertIn('errors', response.data) - self.assertIn('request_id', response.data) - - def test_authentication_failed_401(self): - """Test AuthenticationFailed returns 401 with unified format""" - request = self.factory.get('/test/') - exc = AuthenticationFailed("Authentication required") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], 'Authentication required') - self.assertIn('request_id', response.data) - - def test_permission_denied_403(self): - """Test PermissionDenied returns 403 with unified format""" - request = self.factory.get('/test/') - exc = PermissionDenied("Permission denied") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], 'Permission denied') - self.assertIn('request_id', response.data) - - def test_not_found_404(self): - """Test NotFound returns 404 with unified format""" - request = self.factory.get('/test/') - exc = NotFound("Resource not found") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], 'Resource not found') - self.assertIn('request_id', response.data) - - def test_throttled_429(self): - """Test Throttled returns 429 with unified format""" - request = self.factory.get('/test/') - exc = Throttled() - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS) - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], 'Rate limit exceeded') - self.assertIn('request_id', response.data) - - def test_method_not_allowed_405(self): - """Test MethodNotAllowed returns 405 with unified format""" - request = self.factory.post('/test/') - exc = MethodNotAllowed("POST") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) - self.assertFalse(response.data['success']) - self.assertIn('error', response.data) - self.assertIn('request_id', response.data) - - def test_unhandled_exception_500(self): - """Test unhandled exception returns 500 with unified format""" - request = self.factory.get('/test/') - exc = ValueError("Unexpected error") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], 'Internal server error') - self.assertIn('request_id', response.data) - - def test_exception_handler_includes_request_id(self): - """Test exception handler includes request_id in response""" - request = self.factory.get('/test/') - request.request_id = 'test-request-id-exception' - exc = ValidationError("Test error") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertIn('request_id', response.data) - self.assertEqual(response.data['request_id'], 'test-request-id-exception') - - def test_exception_handler_debug_mode(self): - """Test exception handler includes debug info in DEBUG mode""" - from django.conf import settings - original_debug = settings.DEBUG - - try: - settings.DEBUG = True - request = self.factory.get('/test/') - exc = ValueError("Test error") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertIn('debug', response.data) - self.assertIn('exception_type', response.data['debug']) - self.assertIn('exception_message', response.data['debug']) - self.assertIn('view', response.data['debug']) - self.assertIn('path', response.data['debug']) - self.assertIn('method', response.data['debug']) - finally: - settings.DEBUG = original_debug - - def test_exception_handler_no_debug_mode(self): - """Test exception handler excludes debug info when DEBUG=False""" - from django.conf import settings - original_debug = settings.DEBUG - - try: - settings.DEBUG = False - request = self.factory.get('/test/') - exc = ValueError("Test error") - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertNotIn('debug', response.data) - finally: - settings.DEBUG = original_debug - - def test_field_specific_validation_errors(self): - """Test field-specific validation errors are included""" - request = self.factory.post('/test/', {}) - exc = ValidationError({ - "email": ["Invalid email format"], - "password": ["Password too short", "Password must contain numbers"] - }) - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertIn('errors', response.data) - self.assertIn('email', response.data['errors']) - self.assertIn('password', response.data['errors']) - self.assertEqual(len(response.data['errors']['password']), 2) - - def test_non_field_validation_errors(self): - """Test non-field validation errors are handled""" - request = self.factory.post('/test/', {}) - exc = ValidationError({"non_field_errors": ["General validation error"]}) - context = {'request': request, 'view': self.view} - - response = custom_exception_handler(exc, context) - - self.assertIn('errors', response.data) - self.assertIn('non_field_errors', response.data['errors']) - diff --git a/backend/igny8_core/api/tests/test_integration_auth.py b/backend/igny8_core/api/tests/test_integration_auth.py deleted file mode 100644 index c7a64a06..00000000 --- a/backend/igny8_core/api/tests/test_integration_auth.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -Integration tests for Auth module endpoints -Tests login, register, user management return unified format -""" -from rest_framework import status -from django.test import TestCase -from rest_framework.test import APIClient -from igny8_core.auth.models import User, Account, Plan - - -class AuthIntegrationTestCase(TestCase): - """Integration tests for Auth module""" - - def setUp(self): - """Set up test fixtures""" - self.client = APIClient() - - # Create test plan and account - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create test user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create test account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - def assert_unified_response_format(self, response, expected_success=True): - """Assert response follows unified format""" - self.assertIn('success', response.data) - self.assertEqual(response.data['success'], expected_success) - - if expected_success: - self.assertTrue('data' in response.data or 'results' in response.data) - else: - self.assertIn('error', response.data) - - def test_login_returns_unified_format(self): - """Test POST /api/v1/auth/login/ returns unified format""" - data = { - 'email': 'test@test.com', - 'password': 'testpass123' - } - response = self.client.post('/api/v1/auth/login/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - self.assertIn('user', response.data['data']) - self.assertIn('access', response.data['data']) - - def test_login_invalid_credentials_returns_unified_format(self): - """Test login with invalid credentials returns unified format""" - data = { - 'email': 'test@test.com', - 'password': 'wrongpassword' - } - response = self.client.post('/api/v1/auth/login/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('request_id', response.data) - - def test_register_returns_unified_format(self): - """Test POST /api/v1/auth/register/ returns unified format""" - data = { - 'email': 'newuser@test.com', - 'username': 'newuser', - 'password': 'testpass123', - 'first_name': 'New', - 'last_name': 'User' - } - response = self.client.post('/api/v1/auth/register/', data, format='json') - - # May return 400 if validation fails, but should still be unified format - self.assertIn(response.status_code, [status.HTTP_201_CREATED, status.HTTP_400_BAD_REQUEST]) - self.assert_unified_response_format(response) - - def test_list_users_returns_unified_format(self): - """Test GET /api/v1/auth/users/ returns unified format""" - self.client.force_authenticate(user=self.user) - response = self.client.get('/api/v1/auth/users/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_list_accounts_returns_unified_format(self): - """Test GET /api/v1/auth/accounts/ returns unified format""" - self.client.force_authenticate(user=self.user) - response = self.client.get('/api/v1/auth/accounts/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_list_sites_returns_unified_format(self): - """Test GET /api/v1/auth/sites/ returns unified format""" - self.client.force_authenticate(user=self.user) - response = self.client.get('/api/v1/auth/sites/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_unauthorized_returns_unified_format(self): - """Test 401 errors return unified format""" - # Don't authenticate - response = self.client.get('/api/v1/auth/users/') - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('request_id', response.data) - diff --git a/backend/igny8_core/api/tests/test_integration_base.py b/backend/igny8_core/api/tests/test_integration_base.py deleted file mode 100644 index a86df97c..00000000 --- a/backend/igny8_core/api/tests/test_integration_base.py +++ /dev/null @@ -1,111 +0,0 @@ -""" -Base test class for integration tests -Provides common fixtures and utilities -""" -from django.test import TestCase -from rest_framework.test import APIClient -from rest_framework import status -from igny8_core.auth.models import User, Account, Plan, Site, Sector, Industry, IndustrySector, SeedKeyword - - -class IntegrationTestBase(TestCase): - """Base class for integration tests with common fixtures""" - - def setUp(self): - """Set up test fixtures""" - self.client = APIClient() - - # Create test plan - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create test user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create test account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - # Create site - self.site = Site.objects.create( - name="Test Site", - slug="test-site", - account=self.account, - industry=self.industry - ) - - # Create sector (Sector needs industry_sector reference) - self.sector = Sector.objects.create( - name="Test Sector", - slug="test-sector", - site=self.site, - account=self.account, - industry_sector=self.industry_sector - ) - - # Create seed keyword - self.seed_keyword = SeedKeyword.objects.create( - keyword="test keyword", - industry=self.industry, - sector=self.industry_sector, - volume=1000, - difficulty=50, - country="US" - ) - - # Authenticate client - self.client.force_authenticate(user=self.user) - - # Set account on request (simulating middleware) - self.client.force_authenticate(user=self.user) - - def assert_unified_response_format(self, response, expected_success=True): - """Assert response follows unified format""" - self.assertIn('success', response.data) - self.assertEqual(response.data['success'], expected_success) - - if expected_success: - # Success responses should have data or results - self.assertTrue('data' in response.data or 'results' in response.data) - else: - # Error responses should have error - self.assertIn('error', response.data) - - def assert_paginated_response(self, response): - """Assert response is a paginated response""" - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('success', response.data) - self.assertIn('count', response.data) - self.assertIn('results', response.data) - self.assertIn('next', response.data) - self.assertIn('previous', response.data) - diff --git a/backend/igny8_core/api/tests/test_integration_billing.py b/backend/igny8_core/api/tests/test_integration_billing.py deleted file mode 100644 index 3c5bc49f..00000000 --- a/backend/igny8_core/api/tests/test_integration_billing.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Integration tests for Billing module endpoints -Tests credit balance, usage, transactions return unified format -""" -from rest_framework import status -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class BillingIntegrationTestCase(IntegrationTestBase): - """Integration tests for Billing module""" - - def test_credit_balance_returns_unified_format(self): - """Test GET /api/v1/billing/credits/balance/balance/ returns unified format""" - response = self.client.get('/api/v1/billing/credits/balance/balance/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_credit_usage_returns_unified_format(self): - """Test GET /api/v1/billing/credits/usage/ returns unified format""" - response = self.client.get('/api/v1/billing/credits/usage/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_usage_summary_returns_unified_format(self): - """Test GET /api/v1/billing/credits/usage/summary/ returns unified format""" - response = self.client.get('/api/v1/billing/credits/usage/summary/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_usage_limits_returns_unified_format(self): - """Test GET /api/v1/billing/credits/usage/limits/ returns unified format""" - response = self.client.get('/api/v1/billing/credits/usage/limits/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_transactions_returns_unified_format(self): - """Test GET /api/v1/billing/credits/transactions/ returns unified format""" - response = self.client.get('/api/v1/billing/credits/transactions/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - diff --git a/backend/igny8_core/api/tests/test_integration_errors.py b/backend/igny8_core/api/tests/test_integration_errors.py deleted file mode 100644 index 7489ed1f..00000000 --- a/backend/igny8_core/api/tests/test_integration_errors.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -Integration tests for error scenarios -Tests 400, 401, 403, 404, 429, 500 responses return unified format -""" -from rest_framework import status -from django.test import TestCase -from rest_framework.test import APIClient -from igny8_core.auth.models import User, Account, Plan -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class ErrorScenariosTestCase(IntegrationTestBase): - """Integration tests for error scenarios""" - - def test_400_bad_request_returns_unified_format(self): - """Test 400 Bad Request returns unified format""" - # Invalid data - data = {'invalid': 'data'} - response = self.client.post('/api/v1/planner/keywords/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('errors', response.data) - self.assertIn('request_id', response.data) - - def test_401_unauthorized_returns_unified_format(self): - """Test 401 Unauthorized returns unified format""" - # Create unauthenticated client - unauthenticated_client = APIClient() - response = unauthenticated_client.get('/api/v1/planner/keywords/') - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertEqual(response.data['error'], 'Authentication required') - self.assertIn('request_id', response.data) - - def test_403_forbidden_returns_unified_format(self): - """Test 403 Forbidden returns unified format""" - # Create viewer user (limited permissions) - viewer_user = User.objects.create_user( - username='viewer', - email='viewer@test.com', - password='testpass123', - role='viewer', - account=self.account - ) - - viewer_client = APIClient() - viewer_client.force_authenticate(user=viewer_user) - - # Try to access admin-only endpoint (if exists) - # For now, test with a protected endpoint that requires editor+ - response = viewer_client.post('/api/v1/planner/keywords/auto_cluster/', {}, format='json') - - # May return 400 (validation) or 403 (permission), both should be unified - self.assertIn(response.status_code, [status.HTTP_400_BAD_REQUEST, status.HTTP_403_FORBIDDEN]) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('request_id', response.data) - - def test_404_not_found_returns_unified_format(self): - """Test 404 Not Found returns unified format""" - response = self.client.get('/api/v1/planner/keywords/99999/') - - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertEqual(response.data['error'], 'Resource not found') - self.assertIn('request_id', response.data) - - def test_404_invalid_endpoint_returns_unified_format(self): - """Test 404 for invalid endpoint returns unified format""" - response = self.client.get('/api/v1/nonexistent/endpoint/') - - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - # DRF may return different format for URL not found, but our handler should catch it - if 'success' in response.data: - self.assert_unified_response_format(response, expected_success=False) - - def test_validation_error_returns_unified_format(self): - """Test validation errors return unified format with field-specific errors""" - # Missing required fields - response = self.client.post('/api/v1/planner/keywords/', {}, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('errors', response.data) - # Should have field-specific errors - self.assertIsInstance(response.data['errors'], dict) - diff --git a/backend/igny8_core/api/tests/test_integration_pagination.py b/backend/igny8_core/api/tests/test_integration_pagination.py deleted file mode 100644 index daefe34c..00000000 --- a/backend/igny8_core/api/tests/test_integration_pagination.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -Integration tests for pagination -Tests paginated responses across all modules return unified format -""" -from rest_framework import status -from igny8_core.api.tests.test_integration_base import IntegrationTestBase -from igny8_core.modules.planner.models import Keywords -from igny8_core.auth.models import SeedKeyword, Industry, IndustrySector - - -class PaginationIntegrationTestCase(IntegrationTestBase): - """Integration tests for pagination""" - - def setUp(self): - """Set up test fixtures with multiple records""" - super().setUp() - - # Create multiple keywords for pagination testing - for i in range(15): - Keywords.objects.create( - seed_keyword=self.seed_keyword, - site=self.site, - sector=self.sector, - account=self.account, - status='active' - ) - - def test_pagination_default_page_size(self): - """Test pagination with default page size""" - response = self.client.get('/api/v1/planner/keywords/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - self.assertEqual(response.data['count'], 15) - self.assertLessEqual(len(response.data['results']), 10) # Default page size - self.assertIsNotNone(response.data['next']) # Should have next page - - def test_pagination_custom_page_size(self): - """Test pagination with custom page size""" - response = self.client.get('/api/v1/planner/keywords/?page_size=5') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - self.assertEqual(response.data['count'], 15) - self.assertEqual(len(response.data['results']), 5) - self.assertIsNotNone(response.data['next']) - - def test_pagination_page_parameter(self): - """Test pagination with page parameter""" - response = self.client.get('/api/v1/planner/keywords/?page=2&page_size=5') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - self.assertEqual(response.data['count'], 15) - self.assertEqual(len(response.data['results']), 5) - self.assertIsNotNone(response.data['previous']) - - def test_pagination_max_page_size(self): - """Test pagination respects max page size""" - response = self.client.get('/api/v1/planner/keywords/?page_size=200') # Exceeds max of 100 - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - self.assertLessEqual(len(response.data['results']), 100) # Should be capped at 100 - - def test_pagination_empty_results(self): - """Test pagination with empty results""" - # Use a filter that returns no results - response = self.client.get('/api/v1/planner/keywords/?status=nonexistent') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - self.assertEqual(response.data['count'], 0) - self.assertEqual(len(response.data['results']), 0) - self.assertIsNone(response.data['next']) - self.assertIsNone(response.data['previous']) - - def test_pagination_includes_success_field(self): - """Test paginated responses include success field""" - response = self.client.get('/api/v1/planner/keywords/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertIn('success', response.data) - self.assertTrue(response.data['success']) - - def test_pagination_clusters(self): - """Test pagination works for clusters endpoint""" - response = self.client.get('/api/v1/planner/clusters/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_pagination_ideas(self): - """Test pagination works for ideas endpoint""" - response = self.client.get('/api/v1/planner/ideas/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_pagination_tasks(self): - """Test pagination works for tasks endpoint""" - response = self.client.get('/api/v1/writer/tasks/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_pagination_content(self): - """Test pagination works for content endpoint""" - response = self.client.get('/api/v1/writer/content/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - diff --git a/backend/igny8_core/api/tests/test_integration_planner.py b/backend/igny8_core/api/tests/test_integration_planner.py deleted file mode 100644 index 75138f2b..00000000 --- a/backend/igny8_core/api/tests/test_integration_planner.py +++ /dev/null @@ -1,160 +0,0 @@ -""" -Integration tests for Planner module endpoints -Tests CRUD operations and AI actions return unified format -""" -from rest_framework import status -from igny8_core.api.tests.test_integration_base import IntegrationTestBase -from igny8_core.modules.planner.models import Keywords, Clusters, ContentIdeas - - -class PlannerIntegrationTestCase(IntegrationTestBase): - """Integration tests for Planner module""" - - def test_list_keywords_returns_unified_format(self): - """Test GET /api/v1/planner/keywords/ returns unified format""" - response = self.client.get('/api/v1/planner/keywords/') - - # May get 429 if rate limited - both should have unified format - if response.status_code == status.HTTP_429_TOO_MANY_REQUESTS: - self.assert_unified_response_format(response, expected_success=False) - else: - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_create_keyword_returns_unified_format(self): - """Test POST /api/v1/planner/keywords/ returns unified format""" - data = { - 'seed_keyword_id': self.seed_keyword.id, - 'site_id': self.site.id, - 'sector_id': self.sector.id, - 'status': 'active' - } - response = self.client.post('/api/v1/planner/keywords/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - self.assertIn('id', response.data['data']) - - def test_retrieve_keyword_returns_unified_format(self): - """Test GET /api/v1/planner/keywords/{id}/ returns unified format""" - keyword = Keywords.objects.create( - seed_keyword=self.seed_keyword, - site=self.site, - sector=self.sector, - account=self.account, - status='active' - ) - - response = self.client.get(f'/api/v1/planner/keywords/{keyword.id}/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - self.assertEqual(response.data['data']['id'], keyword.id) - - def test_update_keyword_returns_unified_format(self): - """Test PUT /api/v1/planner/keywords/{id}/ returns unified format""" - keyword = Keywords.objects.create( - seed_keyword=self.seed_keyword, - site=self.site, - sector=self.sector, - account=self.account, - status='active' - ) - - data = { - 'seed_keyword_id': self.seed_keyword.id, - 'site_id': self.site.id, - 'sector_id': self.sector.id, - 'status': 'archived' - } - response = self.client.put(f'/api/v1/planner/keywords/{keyword.id}/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_delete_keyword_returns_unified_format(self): - """Test DELETE /api/v1/planner/keywords/{id}/ returns unified format""" - keyword = Keywords.objects.create( - seed_keyword=self.seed_keyword, - site=self.site, - sector=self.sector, - account=self.account, - status='active' - ) - - response = self.client.delete(f'/api/v1/planner/keywords/{keyword.id}/') - - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - - def test_list_clusters_returns_unified_format(self): - """Test GET /api/v1/planner/clusters/ returns unified format""" - response = self.client.get('/api/v1/planner/clusters/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_create_cluster_returns_unified_format(self): - """Test POST /api/v1/planner/clusters/ returns unified format""" - data = { - 'name': 'Test Cluster', - 'description': 'Test description', - 'site_id': self.site.id, - 'sector_id': self.sector.id, - 'status': 'active' - } - response = self.client.post('/api/v1/planner/clusters/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_list_ideas_returns_unified_format(self): - """Test GET /api/v1/planner/ideas/ returns unified format""" - response = self.client.get('/api/v1/planner/ideas/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_auto_cluster_returns_unified_format(self): - """Test POST /api/v1/planner/keywords/auto_cluster/ returns unified format""" - keyword = Keywords.objects.create( - seed_keyword=self.seed_keyword, - site=self.site, - sector=self.sector, - account=self.account, - status='active' - ) - - data = { - 'ids': [keyword.id], - 'sector_id': self.sector.id - } - response = self.client.post('/api/v1/planner/keywords/auto_cluster/', data, format='json') - - # Should return either task_id (async) or success response - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_202_ACCEPTED]) - self.assert_unified_response_format(response, expected_success=True) - - def test_keyword_validation_error_returns_unified_format(self): - """Test validation errors return unified format""" - # Missing required fields - response = self.client.post('/api/v1/planner/keywords/', {}, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('errors', response.data) - self.assertIn('request_id', response.data) - - def test_keyword_not_found_returns_unified_format(self): - """Test 404 errors return unified format""" - response = self.client.get('/api/v1/planner/keywords/99999/') - - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('request_id', response.data) - diff --git a/backend/igny8_core/api/tests/test_integration_rate_limiting.py b/backend/igny8_core/api/tests/test_integration_rate_limiting.py deleted file mode 100644 index 7792351a..00000000 --- a/backend/igny8_core/api/tests/test_integration_rate_limiting.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -Integration tests for rate limiting -Tests throttle headers and 429 responses -""" -from rest_framework import status -from django.test import TestCase, override_settings -from rest_framework.test import APIClient -from igny8_core.api.tests.test_integration_base import IntegrationTestBase -from igny8_core.auth.models import User, Account, Plan - - -class RateLimitingIntegrationTestCase(IntegrationTestBase): - """Integration tests for rate limiting""" - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_throttle_headers_present(self): - """Test throttle headers are present in responses""" - response = self.client.get('/api/v1/planner/keywords/') - - # May get 429 if rate limited, or 200 if bypassed - both are valid - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_429_TOO_MANY_REQUESTS]) - # Throttle headers should be present - # Note: In test environment, throttling may be bypassed, but headers should still be set - # We check if headers exist (they may not be set if throttling is bypassed in tests) - if 'X-Throttle-Limit' in response: - self.assertIn('X-Throttle-Limit', response) - self.assertIn('X-Throttle-Remaining', response) - self.assertIn('X-Throttle-Reset', response) - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_rate_limit_bypass_for_admin(self): - """Test rate limiting is bypassed for admin users""" - # Create admin user - admin_user = User.objects.create_user( - username='admin', - email='admin@test.com', - password='testpass123', - role='admin', - account=self.account - ) - - admin_client = APIClient() - admin_client.force_authenticate(user=admin_user) - - # Make multiple requests - should not be throttled - for i in range(15): - response = admin_client.get('/api/v1/planner/keywords/') - self.assertEqual(response.status_code, status.HTTP_200_OK) - # Should not get 429 - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_rate_limit_bypass_for_system_account(self): - """Test rate limiting is bypassed for system account users""" - # Create system account - system_account = Account.objects.create( - name="AWS Admin", - slug="aws-admin", - plan=self.plan - ) - - system_user = User.objects.create_user( - username='system', - email='system@test.com', - password='testpass123', - role='viewer', - account=system_account - ) - - system_client = APIClient() - system_client.force_authenticate(user=system_user) - - # Make multiple requests - should not be throttled - for i in range(15): - response = system_client.get('/api/v1/planner/keywords/') - self.assertEqual(response.status_code, status.HTTP_200_OK) - # Should not get 429 - - @override_settings(DEBUG=True) - def test_rate_limit_bypass_in_debug_mode(self): - """Test rate limiting is bypassed in DEBUG mode""" - # Make multiple requests - should not be throttled in DEBUG mode - for i in range(15): - response = self.client.get('/api/v1/planner/keywords/') - self.assertEqual(response.status_code, status.HTTP_200_OK) - # Should not get 429 - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=True) - def test_rate_limit_bypass_with_env_flag(self): - """Test rate limiting is bypassed when IGNY8_DEBUG_THROTTLE=True""" - # Make multiple requests - should not be throttled - for i in range(15): - response = self.client.get('/api/v1/planner/keywords/') - self.assertEqual(response.status_code, status.HTTP_200_OK) - # Should not get 429 - - def test_different_throttle_scopes(self): - """Test different endpoints have different throttle scopes""" - # Planner endpoints - may get 429 if rate limited - response = self.client.get('/api/v1/planner/keywords/') - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_429_TOO_MANY_REQUESTS]) - - # Writer endpoints - may get 429 if rate limited - response = self.client.get('/api/v1/writer/tasks/') - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_429_TOO_MANY_REQUESTS]) - - # System endpoints - may get 429 if rate limited - response = self.client.get('/api/v1/system/prompts/') - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_429_TOO_MANY_REQUESTS]) - - # Billing endpoints - may get 429 if rate limited - response = self.client.get('/api/v1/billing/credits/balance/balance/') - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_429_TOO_MANY_REQUESTS]) - diff --git a/backend/igny8_core/api/tests/test_integration_system.py b/backend/igny8_core/api/tests/test_integration_system.py deleted file mode 100644 index 32c9348f..00000000 --- a/backend/igny8_core/api/tests/test_integration_system.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Integration tests for System module endpoints -Tests settings, prompts, integrations return unified format -""" -from rest_framework import status -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class SystemIntegrationTestCase(IntegrationTestBase): - """Integration tests for System module""" - - def test_system_status_returns_unified_format(self): - """Test GET /api/v1/system/status/ returns unified format""" - response = self.client.get('/api/v1/system/status/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_list_prompts_returns_unified_format(self): - """Test GET /api/v1/system/prompts/ returns unified format""" - response = self.client.get('/api/v1/system/prompts/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_get_prompt_by_type_returns_unified_format(self): - """Test GET /api/v1/system/prompts/by_type/{type}/ returns unified format""" - response = self.client.get('/api/v1/system/prompts/by_type/clustering/') - - # May return 404 if no prompt exists, but should still be unified format - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) - self.assert_unified_response_format(response) - - def test_list_account_settings_returns_unified_format(self): - """Test GET /api/v1/system/settings/account/ returns unified format""" - response = self.client.get('/api/v1/system/settings/account/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_get_integration_settings_returns_unified_format(self): - """Test GET /api/v1/system/settings/integrations/{pk}/ returns unified format""" - response = self.client.get('/api/v1/system/settings/integrations/openai/') - - # May return 404 if not configured, but should still be unified format - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_404_NOT_FOUND]) - self.assert_unified_response_format(response) - diff --git a/backend/igny8_core/api/tests/test_integration_writer.py b/backend/igny8_core/api/tests/test_integration_writer.py deleted file mode 100644 index 7dced2ca..00000000 --- a/backend/igny8_core/api/tests/test_integration_writer.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Integration tests for Writer module endpoints -Tests CRUD operations and AI actions return unified format -""" -from rest_framework import status -from igny8_core.api.tests.test_integration_base import IntegrationTestBase -from igny8_core.modules.writer.models import Tasks, Content, Images - - -class WriterIntegrationTestCase(IntegrationTestBase): - """Integration tests for Writer module""" - - def test_list_tasks_returns_unified_format(self): - """Test GET /api/v1/writer/tasks/ returns unified format""" - response = self.client.get('/api/v1/writer/tasks/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_create_task_returns_unified_format(self): - """Test POST /api/v1/writer/tasks/ returns unified format""" - data = { - 'title': 'Test Task', - 'site_id': self.site.id, - 'sector_id': self.sector.id, - 'status': 'pending' - } - response = self.client.post('/api/v1/writer/tasks/', data, format='json') - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assert_unified_response_format(response, expected_success=True) - self.assertIn('data', response.data) - - def test_list_content_returns_unified_format(self): - """Test GET /api/v1/writer/content/ returns unified format""" - response = self.client.get('/api/v1/writer/content/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_list_images_returns_unified_format(self): - """Test GET /api/v1/writer/images/ returns unified format""" - response = self.client.get('/api/v1/writer/images/') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assert_paginated_response(response) - - def test_create_image_returns_unified_format(self): - """Test POST /api/v1/writer/images/ returns unified format""" - data = { - 'image_type': 'featured', - 'site_id': self.site.id, - 'sector_id': self.sector.id, - 'status': 'pending' - } - response = self.client.post('/api/v1/writer/images/', data, format='json') - - # May return 400 if site/sector validation fails, but should still be unified format - self.assertIn(response.status_code, [status.HTTP_201_CREATED, status.HTTP_400_BAD_REQUEST]) - self.assert_unified_response_format(response) - - def test_task_validation_error_returns_unified_format(self): - """Test validation errors return unified format""" - response = self.client.post('/api/v1/writer/tasks/', {}, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assert_unified_response_format(response, expected_success=False) - self.assertIn('error', response.data) - self.assertIn('errors', response.data) - diff --git a/backend/igny8_core/api/tests/test_permissions.py b/backend/igny8_core/api/tests/test_permissions.py deleted file mode 100644 index 3ed15482..00000000 --- a/backend/igny8_core/api/tests/test_permissions.py +++ /dev/null @@ -1,313 +0,0 @@ -""" -Unit tests for permission classes -Tests IsAuthenticatedAndActive, HasTenantAccess, IsViewerOrAbove, IsEditorOrAbove, IsAdminOrOwner -""" -from django.test import TestCase -from rest_framework.test import APIRequestFactory -from rest_framework.views import APIView -from igny8_core.api.permissions import ( - IsAuthenticatedAndActive, HasTenantAccess, IsViewerOrAbove, - IsEditorOrAbove, IsAdminOrOwner -) -from igny8_core.auth.models import User, Account, Plan - - -class PermissionsTestCase(TestCase): - """Test cases for permission classes""" - - def setUp(self): - """Set up test fixtures""" - self.factory = APIRequestFactory() - self.view = APIView() - - # Create test plan - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create owner user first (Account needs owner) - self.owner_user = User.objects.create_user( - username='owner', - email='owner@test.com', - password='testpass123', - role='owner' - ) - - # Create test account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.owner_user - ) - - # Update owner user to have account - self.owner_user.account = self.account - self.owner_user.save() - - self.admin_user = User.objects.create_user( - username='admin', - email='admin@test.com', - password='testpass123', - role='admin', - account=self.account - ) - - self.editor_user = User.objects.create_user( - username='editor', - email='editor@test.com', - password='testpass123', - role='editor', - account=self.account - ) - - self.viewer_user = User.objects.create_user( - username='viewer', - email='viewer@test.com', - password='testpass123', - role='viewer', - account=self.account - ) - - # Create another account for tenant isolation testing - self.other_owner = User.objects.create_user( - username='other_owner', - email='other_owner@test.com', - password='testpass123', - role='owner' - ) - - self.other_account = Account.objects.create( - name="Other Account", - slug="other-account", - plan=self.plan, - owner=self.other_owner - ) - - self.other_owner.account = self.other_account - self.other_owner.save() - - self.other_user = User.objects.create_user( - username='other', - email='other@test.com', - password='testpass123', - role='owner', - account=self.other_account - ) - - def test_is_authenticated_and_active_authenticated(self): - """Test IsAuthenticatedAndActive allows authenticated users""" - permission = IsAuthenticatedAndActive() - request = self.factory.get('/test/') - request.user = self.owner_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_authenticated_and_active_unauthenticated(self): - """Test IsAuthenticatedAndActive denies unauthenticated users""" - permission = IsAuthenticatedAndActive() - request = self.factory.get('/test/') - request.user = None - - result = permission.has_permission(request, self.view) - self.assertFalse(result) - - def test_is_authenticated_and_active_inactive_user(self): - """Test IsAuthenticatedAndActive denies inactive users""" - permission = IsAuthenticatedAndActive() - self.owner_user.is_active = False - self.owner_user.save() - - request = self.factory.get('/test/') - request.user = self.owner_user - - result = permission.has_permission(request, self.view) - self.assertFalse(result) - - def test_has_tenant_access_same_account(self): - """Test HasTenantAccess allows users from same account""" - permission = HasTenantAccess() - request = self.factory.get('/test/') - request.user = self.owner_user - request.account = self.account - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_has_tenant_access_different_account(self): - """Test HasTenantAccess denies users from different account""" - permission = HasTenantAccess() - request = self.factory.get('/test/') - request.user = self.owner_user - request.account = self.other_account - - result = permission.has_permission(request, self.view) - self.assertFalse(result) - - def test_has_tenant_access_admin_bypass(self): - """Test HasTenantAccess allows admin/developer to bypass""" - permission = HasTenantAccess() - request = self.factory.get('/test/') - request.user = self.admin_user - request.account = self.other_account # Different account - - result = permission.has_permission(request, self.view) - self.assertTrue(result) # Admin should bypass - - def test_has_tenant_access_system_account(self): - """Test HasTenantAccess allows system account users to bypass""" - # Create system account owner - system_owner = User.objects.create_user( - username='system_owner_test', - email='system_owner_test@test.com', - password='testpass123', - role='owner' - ) - - # Create system account - system_account = Account.objects.create( - name="AWS Admin", - slug="aws-admin", - plan=self.plan, - owner=system_owner - ) - - system_owner.account = system_account - system_owner.save() - - system_user = User.objects.create_user( - username='system', - email='system@test.com', - password='testpass123', - role='viewer', - account=system_account - ) - - permission = HasTenantAccess() - request = self.factory.get('/test/') - request.user = system_user - request.account = self.account # Different account - - result = permission.has_permission(request, self.view) - self.assertTrue(result) # System account user should bypass - - def test_is_viewer_or_above_viewer(self): - """Test IsViewerOrAbove allows viewer role""" - permission = IsViewerOrAbove() - request = self.factory.get('/test/') - request.user = self.viewer_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_viewer_or_above_editor(self): - """Test IsViewerOrAbove allows editor role""" - permission = IsViewerOrAbove() - request = self.factory.get('/test/') - request.user = self.editor_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_viewer_or_above_admin(self): - """Test IsViewerOrAbove allows admin role""" - permission = IsViewerOrAbove() - request = self.factory.get('/test/') - request.user = self.admin_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_viewer_or_above_owner(self): - """Test IsViewerOrAbove allows owner role""" - permission = IsViewerOrAbove() - request = self.factory.get('/test/') - request.user = self.owner_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_editor_or_above_viewer_denied(self): - """Test IsEditorOrAbove denies viewer role""" - permission = IsEditorOrAbove() - request = self.factory.get('/test/') - request.user = self.viewer_user - - result = permission.has_permission(request, self.view) - self.assertFalse(result) - - def test_is_editor_or_above_editor_allowed(self): - """Test IsEditorOrAbove allows editor role""" - permission = IsEditorOrAbove() - request = self.factory.get('/test/') - request.user = self.editor_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_editor_or_above_admin_allowed(self): - """Test IsEditorOrAbove allows admin role""" - permission = IsEditorOrAbove() - request = self.factory.get('/test/') - request.user = self.admin_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_admin_or_owner_viewer_denied(self): - """Test IsAdminOrOwner denies viewer role""" - permission = IsAdminOrOwner() - request = self.factory.get('/test/') - request.user = self.viewer_user - - result = permission.has_permission(request, self.view) - self.assertFalse(result) - - def test_is_admin_or_owner_editor_denied(self): - """Test IsAdminOrOwner denies editor role""" - permission = IsAdminOrOwner() - request = self.factory.get('/test/') - request.user = self.editor_user - - result = permission.has_permission(request, self.view) - self.assertFalse(result) - - def test_is_admin_or_owner_admin_allowed(self): - """Test IsAdminOrOwner allows admin role""" - permission = IsAdminOrOwner() - request = self.factory.get('/test/') - request.user = self.admin_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_is_admin_or_owner_owner_allowed(self): - """Test IsAdminOrOwner allows owner role""" - permission = IsAdminOrOwner() - request = self.factory.get('/test/') - request.user = self.owner_user - - result = permission.has_permission(request, self.view) - self.assertTrue(result) - - def test_all_permissions_unauthenticated_denied(self): - """Test all permissions deny unauthenticated users""" - permissions = [ - IsAuthenticatedAndActive(), - HasTenantAccess(), - IsViewerOrAbove(), - IsEditorOrAbove(), - IsAdminOrOwner() - ] - - request = self.factory.get('/test/') - request.user = None - - for permission in permissions: - result = permission.has_permission(request, self.view) - self.assertFalse(result, f"{permission.__class__.__name__} should deny unauthenticated users") - diff --git a/backend/igny8_core/api/tests/test_response.py b/backend/igny8_core/api/tests/test_response.py deleted file mode 100644 index 353e9c9b..00000000 --- a/backend/igny8_core/api/tests/test_response.py +++ /dev/null @@ -1,206 +0,0 @@ -""" -Unit tests for response helper functions -Tests success_response, error_response, paginated_response -""" -from django.test import TestCase, RequestFactory -from rest_framework import status -from igny8_core.api.response import success_response, error_response, paginated_response, get_request_id - - -class ResponseHelpersTestCase(TestCase): - """Test cases for response helper functions""" - - def setUp(self): - """Set up test fixtures""" - self.factory = RequestFactory() - - def test_success_response_with_data(self): - """Test success_response with data""" - data = {"id": 1, "name": "Test"} - response = success_response(data=data, message="Success") - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertEqual(response.data['data'], data) - self.assertEqual(response.data['message'], "Success") - - def test_success_response_without_data(self): - """Test success_response without data""" - response = success_response(message="Success") - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertNotIn('data', response.data) - self.assertEqual(response.data['message'], "Success") - - def test_success_response_with_custom_status(self): - """Test success_response with custom status code""" - data = {"id": 1} - response = success_response(data=data, status_code=status.HTTP_201_CREATED) - - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertTrue(response.data['success']) - self.assertEqual(response.data['data'], data) - - def test_success_response_with_request_id(self): - """Test success_response includes request_id when request provided""" - request = self.factory.get('/test/') - request.request_id = 'test-request-id-123' - - response = success_response(data={"id": 1}, request=request) - - self.assertTrue(response.data['success']) - self.assertEqual(response.data['request_id'], 'test-request-id-123') - - def test_error_response_with_error_message(self): - """Test error_response with error message""" - response = error_response(error="Validation failed") - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], "Validation failed") - - def test_error_response_with_errors_dict(self): - """Test error_response with field-specific errors""" - errors = {"email": ["Invalid email format"], "password": ["Too short"]} - response = error_response(error="Validation failed", errors=errors) - - self.assertFalse(response.data['success']) - self.assertEqual(response.data['error'], "Validation failed") - self.assertEqual(response.data['errors'], errors) - - def test_error_response_status_code_mapping(self): - """Test error_response maps status codes to default error messages""" - # Test 401 - response = error_response(status_code=status.HTTP_401_UNAUTHORIZED) - self.assertEqual(response.data['error'], 'Authentication required') - - # Test 403 - response = error_response(status_code=status.HTTP_403_FORBIDDEN) - self.assertEqual(response.data['error'], 'Permission denied') - - # Test 404 - response = error_response(status_code=status.HTTP_404_NOT_FOUND) - self.assertEqual(response.data['error'], 'Resource not found') - - # Test 409 - response = error_response(status_code=status.HTTP_409_CONFLICT) - self.assertEqual(response.data['error'], 'Conflict') - - # Test 422 - response = error_response(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) - self.assertEqual(response.data['error'], 'Validation failed') - - # Test 429 - response = error_response(status_code=status.HTTP_429_TOO_MANY_REQUESTS) - self.assertEqual(response.data['error'], 'Rate limit exceeded') - - # Test 500 - response = error_response(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) - self.assertEqual(response.data['error'], 'Internal server error') - - def test_error_response_with_request_id(self): - """Test error_response includes request_id when request provided""" - request = self.factory.get('/test/') - request.request_id = 'test-request-id-456' - - response = error_response(error="Error occurred", request=request) - - self.assertFalse(response.data['success']) - self.assertEqual(response.data['request_id'], 'test-request-id-456') - - def test_error_response_with_debug_info(self): - """Test error_response includes debug info when provided""" - debug_info = {"exception_type": "ValueError", "message": "Test error"} - response = error_response(error="Error", debug_info=debug_info) - - self.assertFalse(response.data['success']) - self.assertEqual(response.data['debug'], debug_info) - - def test_paginated_response_with_data(self): - """Test paginated_response with paginated data""" - paginated_data = { - 'count': 100, - 'next': 'http://test.com/api/v1/test/?page=2', - 'previous': None, - 'results': [{"id": 1}, {"id": 2}] - } - response = paginated_response(paginated_data, message="Success") - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertEqual(response.data['count'], 100) - self.assertEqual(response.data['next'], paginated_data['next']) - self.assertEqual(response.data['previous'], None) - self.assertEqual(response.data['results'], paginated_data['results']) - self.assertEqual(response.data['message'], "Success") - - def test_paginated_response_without_message(self): - """Test paginated_response without message""" - paginated_data = { - 'count': 50, - 'next': None, - 'previous': None, - 'results': [] - } - response = paginated_response(paginated_data) - - self.assertTrue(response.data['success']) - self.assertEqual(response.data['count'], 50) - self.assertNotIn('message', response.data) - - def test_paginated_response_with_request_id(self): - """Test paginated_response includes request_id when request provided""" - request = self.factory.get('/test/') - request.request_id = 'test-request-id-789' - - paginated_data = { - 'count': 10, - 'next': None, - 'previous': None, - 'results': [] - } - response = paginated_response(paginated_data, request=request) - - self.assertTrue(response.data['success']) - self.assertEqual(response.data['request_id'], 'test-request-id-789') - - def test_paginated_response_fallback(self): - """Test paginated_response handles non-dict input""" - response = paginated_response(None) - - self.assertTrue(response.data['success']) - self.assertEqual(response.data['count'], 0) - self.assertIsNone(response.data['next']) - self.assertIsNone(response.data['previous']) - self.assertEqual(response.data['results'], []) - - def test_get_request_id_from_request_object(self): - """Test get_request_id retrieves from request.request_id""" - request = self.factory.get('/test/') - request.request_id = 'request-id-from-object' - - request_id = get_request_id(request) - self.assertEqual(request_id, 'request-id-from-object') - - def test_get_request_id_from_headers(self): - """Test get_request_id retrieves from headers""" - request = self.factory.get('/test/', HTTP_X_REQUEST_ID='request-id-from-header') - - request_id = get_request_id(request) - self.assertEqual(request_id, 'request-id-from-header') - - def test_get_request_id_generates_new(self): - """Test get_request_id generates new UUID if not found""" - request = self.factory.get('/test/') - - request_id = get_request_id(request) - self.assertIsNotNone(request_id) - self.assertIsInstance(request_id, str) - # UUID format check - import uuid - try: - uuid.UUID(request_id) - except ValueError: - self.fail("Generated request_id is not a valid UUID") - diff --git a/backend/igny8_core/api/tests/test_throttles.py b/backend/igny8_core/api/tests/test_throttles.py deleted file mode 100644 index 373762c7..00000000 --- a/backend/igny8_core/api/tests/test_throttles.py +++ /dev/null @@ -1,199 +0,0 @@ -""" -Unit tests for rate limiting -Tests DebugScopedRateThrottle with bypass logic -""" -from django.test import TestCase, override_settings -from rest_framework.test import APIRequestFactory -from rest_framework.views import APIView -from igny8_core.api.throttles import DebugScopedRateThrottle -from igny8_core.auth.models import User, Account, Plan - - -class ThrottlesTestCase(TestCase): - """Test cases for rate limiting""" - - def setUp(self): - """Set up test fixtures""" - self.factory = APIRequestFactory() - self.view = APIView() - self.view.throttle_scope = 'planner' - - # Create test plan and account - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create owner user first - self.owner_user = User.objects.create_user( - username='owner', - email='owner@test.com', - password='testpass123', - role='owner' - ) - - # Create test account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.owner_user - ) - - # Update owner user to have account - self.owner_user.account = self.account - self.owner_user.save() - - # Create regular user - self.user = User.objects.create_user( - username='user', - email='user@test.com', - password='testpass123', - role='viewer', - account=self.account - ) - - # Create admin user - self.admin_user = User.objects.create_user( - username='admin', - email='admin@test.com', - password='testpass123', - role='admin', - account=self.account - ) - - # Create system account owner - self.system_owner = User.objects.create_user( - username='system_owner', - email='system_owner@test.com', - password='testpass123', - role='owner' - ) - - # Create system account user - self.system_account = Account.objects.create( - name="AWS Admin", - slug="aws-admin", - plan=self.plan, - owner=self.system_owner - ) - - self.system_owner.account = self.system_account - self.system_owner.save() - - self.system_user = User.objects.create_user( - username='system', - email='system@test.com', - password='testpass123', - role='viewer', - account=self.system_account - ) - - @override_settings(DEBUG=True) - def test_debug_mode_bypass(self): - """Test throttling is bypassed in DEBUG mode""" - throttle = DebugScopedRateThrottle() - request = self.factory.get('/test/') - request.user = self.user - - result = throttle.allow_request(request, self.view) - self.assertTrue(result) # Should bypass in DEBUG mode - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=True) - def test_env_bypass(self): - """Test throttling is bypassed when IGNY8_DEBUG_THROTTLE=True""" - throttle = DebugScopedRateThrottle() - request = self.factory.get('/test/') - request.user = self.user - - result = throttle.allow_request(request, self.view) - self.assertTrue(result) # Should bypass when IGNY8_DEBUG_THROTTLE=True - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_system_account_bypass(self): - """Test throttling is bypassed for system account users""" - throttle = DebugScopedRateThrottle() - request = self.factory.get('/test/') - request.user = self.system_user - - result = throttle.allow_request(request, self.view) - self.assertTrue(result) # System account users should bypass - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_admin_bypass(self): - """Test throttling is bypassed for admin/developer users""" - throttle = DebugScopedRateThrottle() - request = self.factory.get('/test/') - request.user = self.admin_user - - result = throttle.allow_request(request, self.view) - self.assertTrue(result) # Admin users should bypass - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_get_rate(self): - """Test get_rate returns correct rate for scope""" - throttle = DebugScopedRateThrottle() - throttle.scope = 'planner' - - rate = throttle.get_rate() - self.assertIsNotNone(rate) - self.assertIn('/', rate) # Should be in format "60/min" - - @override_settings(DEBUG=False, IGNY8_DEBUG_THROTTLE=False) - def test_get_rate_default_fallback(self): - """Test get_rate falls back to default if scope not found""" - throttle = DebugScopedRateThrottle() - throttle.scope = 'nonexistent_scope' - - rate = throttle.get_rate() - self.assertIsNotNone(rate) - self.assertEqual(rate, '100/min') # Should fallback to default - - def test_parse_rate_minutes(self): - """Test parse_rate correctly parses minutes""" - throttle = DebugScopedRateThrottle() - - num, duration = throttle.parse_rate('60/min') - self.assertEqual(num, 60) - self.assertEqual(duration, 60) - - def test_parse_rate_seconds(self): - """Test parse_rate correctly parses seconds""" - throttle = DebugScopedRateThrottle() - - num, duration = throttle.parse_rate('10/sec') - self.assertEqual(num, 10) - self.assertEqual(duration, 1) - - def test_parse_rate_hours(self): - """Test parse_rate correctly parses hours""" - throttle = DebugScopedRateThrottle() - - num, duration = throttle.parse_rate('100/hour') - self.assertEqual(num, 100) - self.assertEqual(duration, 3600) - - def test_parse_rate_invalid_format(self): - """Test parse_rate handles invalid format gracefully""" - throttle = DebugScopedRateThrottle() - - num, duration = throttle.parse_rate('invalid') - self.assertEqual(num, 100) # Should default to 100 - self.assertEqual(duration, 60) # Should default to 60 seconds (1 min) - - @override_settings(DEBUG=True) - def test_debug_info_set(self): - """Test debug info is set when bypassing in DEBUG mode""" - throttle = DebugScopedRateThrottle() - request = self.factory.get('/test/') - request.user = self.user - - result = throttle.allow_request(request, self.view) - self.assertTrue(result) - self.assertTrue(hasattr(request, '_throttle_debug_info')) - self.assertIn('scope', request._throttle_debug_info) - self.assertIn('rate', request._throttle_debug_info) - self.assertIn('limit', request._throttle_debug_info) - diff --git a/backend/igny8_core/auth/management/commands/cleanup_user_data.py b/backend/igny8_core/auth/management/commands/cleanup_user_data.py new file mode 100644 index 00000000..dc7a1822 --- /dev/null +++ b/backend/igny8_core/auth/management/commands/cleanup_user_data.py @@ -0,0 +1,161 @@ +""" +Management command to clean up all user-generated data (DESTRUCTIVE). +This is used before V1.0 production launch to start with a clean database. + +⚠️ WARNING: This permanently deletes ALL user data! + +Usage: + # DRY RUN (recommended first): + python manage.py cleanup_user_data --dry-run + + # ACTUAL CLEANUP (after reviewing dry-run): + python manage.py cleanup_user_data --confirm +""" +from django.core.management.base import BaseCommand +from django.db import transaction +from django.conf import settings + + +class Command(BaseCommand): + help = 'Clean up all user-generated data (DESTRUCTIVE - for pre-launch cleanup)' + + def add_arguments(self, parser): + parser.add_argument( + '--confirm', + action='store_true', + help='Confirm you want to delete all user data' + ) + parser.add_argument( + '--dry-run', + action='store_true', + help='Show what would be deleted without actually deleting' + ) + parser.add_argument( + '--force-staging', + action='store_true', + help='Treat current environment as staging (bypasses production check)' + ) + + def handle(self, *args, **options): + if not options['confirm'] and not options['dry_run']: + self.stdout.write( + self.style.ERROR('\n⚠️ ERROR: Must use --confirm or --dry-run flag\n') + ) + self.stdout.write('Usage:') + self.stdout.write(' python manage.py cleanup_user_data --dry-run # See what will be deleted') + self.stdout.write(' python manage.py cleanup_user_data --confirm # Actually delete data\n') + return + + # Safety check: Prevent running in production unless explicitly allowed + env = getattr(settings, 'ENVIRONMENT', 'production') + if options.get('force_staging'): + env = 'staging' + self.stdout.write(self.style.WARNING('\n⚠️ FORCE-STAGING: Treating environment as staging\n')) + + if env == 'production' and options['confirm']: + self.stdout.write( + self.style.ERROR('\n⚠️ BLOCKED: Cannot run cleanup in PRODUCTION environment!\n') + ) + self.stdout.write('To allow this, use --force-staging flag or set ENVIRONMENT to "staging" in settings.\n') + return + + # Import models + from igny8_core.auth.models import Site, User + from igny8_core.business.planning.models import Keywords, Clusters, ContentIdeas + from igny8_core.business.content.models import Tasks, Content, Images + from igny8_core.business.publishing.models import PublishingRecord + from igny8_core.business.integration.models import SyncEvent + from igny8_core.business.billing.models import CreditTransaction, CreditUsageLog + from igny8_core.business.notifications.models import Notification + from igny8_core.business.automation.models import AutomationRun + + # Define models to clear (ORDER MATTERS - foreign keys) + # Delete child records before parent records + models_to_clear = [ + ('Notifications', Notification), + ('Credit Usage Logs', CreditUsageLog), + ('Credit Transactions', CreditTransaction), + ('Sync Events', SyncEvent), + ('Publishing Records', PublishingRecord), + ('Automation Runs', AutomationRun), + ('Images', Images), + ('Content', Content), + ('Tasks', Tasks), + ('Content Ideas', ContentIdeas), + ('Clusters', Clusters), + ('Keywords', Keywords), + ('Sites', Site), # Sites should be near last (many foreign keys) + # Note: We do NOT delete User - keep admin users + ] + + if options['dry_run']: + self.stdout.write(self.style.WARNING('\n' + '=' * 70)) + self.stdout.write(self.style.WARNING('DRY RUN - No data will be deleted')) + self.stdout.write(self.style.WARNING('=' * 70 + '\n')) + + total_records = 0 + for name, model in models_to_clear: + count = model.objects.count() + total_records += count + status = '✓' if count > 0 else '·' + self.stdout.write(f' {status} Would delete {count:6d} {name}') + + # Count users (not deleted) + user_count = User.objects.count() + self.stdout.write(f'\n → Keeping {user_count:6d} Users (not deleted)') + + self.stdout.write(f'\n Total records to delete: {total_records:,}') + self.stdout.write('\n' + '=' * 70) + self.stdout.write(self.style.SUCCESS('\nTo proceed with actual deletion, run:')) + self.stdout.write(' python manage.py cleanup_user_data --confirm\n') + return + + # ACTUAL DELETION + self.stdout.write(self.style.ERROR('\n' + '=' * 70)) + self.stdout.write(self.style.ERROR('⚠️ DELETING ALL USER DATA - THIS CANNOT BE UNDONE!')) + self.stdout.write(self.style.ERROR('=' * 70 + '\n')) + + # Final confirmation prompt + confirm_text = input('Type "DELETE ALL DATA" to proceed: ') + if confirm_text != 'DELETE ALL DATA': + self.stdout.write(self.style.WARNING('\nAborted. Data was NOT deleted.\n')) + return + + self.stdout.write('\nProceeding with deletion...\n') + + deleted_counts = {} + failed_deletions = [] + + with transaction.atomic(): + for name, model in models_to_clear: + try: + count = model.objects.count() + if count > 0: + model.objects.all().delete() + deleted_counts[name] = count + self.stdout.write( + self.style.SUCCESS(f'✓ Deleted {count:6d} {name}') + ) + else: + self.stdout.write( + self.style.WARNING(f'· Skipped {count:6d} {name} (already empty)') + ) + except Exception as e: + failed_deletions.append((name, str(e))) + self.stdout.write( + self.style.ERROR(f'✗ Failed to delete {name}: {str(e)}') + ) + + # Summary + total_deleted = sum(deleted_counts.values()) + self.stdout.write('\n' + '=' * 70) + self.stdout.write(self.style.SUCCESS(f'\nUser Data Cleanup Complete!\n')) + self.stdout.write(f' Total records deleted: {total_deleted:,}') + self.stdout.write(f' Failed deletions: {len(failed_deletions)}') + + if failed_deletions: + self.stdout.write(self.style.WARNING('\nFailed deletions:')) + for name, error in failed_deletions: + self.stdout.write(f' - {name}: {error}') + + self.stdout.write('\n' + '=' * 70 + '\n') diff --git a/backend/igny8_core/auth/management/commands/export_system_config.py b/backend/igny8_core/auth/management/commands/export_system_config.py new file mode 100644 index 00000000..ae0f3c51 --- /dev/null +++ b/backend/igny8_core/auth/management/commands/export_system_config.py @@ -0,0 +1,122 @@ +""" +Management command to export system configuration data to JSON files. +This exports Plans, Credit Costs, AI Models, Industries, Sectors, Seed Keywords, etc. + +Usage: + python manage.py export_system_config --output-dir=backups/config +""" +from django.core.management.base import BaseCommand +from django.core import serializers +import json +import os +from datetime import datetime + + +class Command(BaseCommand): + help = 'Export system configuration data to JSON files for V1.0 backup' + + def add_arguments(self, parser): + parser.add_argument( + '--output-dir', + default='backups/config', + help='Output directory for config files (relative to project root)' + ) + + def handle(self, *args, **options): + output_dir = options['output_dir'] + + # Make output_dir absolute if it's relative + if not os.path.isabs(output_dir): + # Get project root (parent of manage.py) + import sys + project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) + output_dir = os.path.join(project_root, '..', output_dir) + + os.makedirs(output_dir, exist_ok=True) + + self.stdout.write(self.style.SUCCESS(f'\nExporting system configuration to: {output_dir}\n')) + + # Import models with correct paths + from igny8_core.auth.models import Plan, Industry, Sector, SeedKeyword + from igny8_core.modules.system.models import AuthorProfile, AIPrompt + from igny8_core.modules.system.global_settings_models import GlobalIntegrationSettings, GlobalAIPrompt + from igny8_core.business.billing.models import CreditCostConfig, AIModelConfig + + # Define what to export + exports = { + 'plans': (Plan.objects.all(), 'Subscription Plans'), + 'credit_costs': (CreditCostConfig.objects.all(), 'Credit Cost Configurations'), + 'ai_models': (AIModelConfig.objects.all(), 'AI Model Configurations'), + 'global_integrations': (GlobalIntegrationSettings.objects.all(), 'Global Integration Settings'), + 'industries': (Industry.objects.all(), 'Industries'), + 'sectors': (Sector.objects.all(), 'Sectors'), + 'seed_keywords': (SeedKeyword.objects.all(), 'Seed Keywords'), + 'author_profiles': (AuthorProfile.objects.all(), 'Author Profiles'), + 'prompts': (AIPrompt.objects.all(), 'AI Prompts'), + 'global_prompts': (GlobalAIPrompt.objects.all(), 'Global AI Prompts'), + } + + successful_exports = [] + failed_exports = [] + + for name, (queryset, description) in exports.items(): + try: + count = queryset.count() + data = serializers.serialize('json', queryset, indent=2) + filepath = os.path.join(output_dir, f'{name}.json') + + with open(filepath, 'w') as f: + f.write(data) + + self.stdout.write( + self.style.SUCCESS(f'✓ Exported {count:4d} {description:30s} → {name}.json') + ) + successful_exports.append(name) + + except Exception as e: + self.stdout.write( + self.style.ERROR(f'✗ Failed to export {description}: {str(e)}') + ) + failed_exports.append((name, str(e))) + + # Export metadata + metadata = { + 'exported_at': datetime.now().isoformat(), + 'django_version': self.get_django_version(), + 'database': self.get_database_info(), + 'successful_exports': successful_exports, + 'failed_exports': failed_exports, + 'export_count': len(successful_exports), + } + + metadata_path = os.path.join(output_dir, 'export_metadata.json') + with open(metadata_path, 'w') as f: + json.dump(metadata, f, indent=2) + + self.stdout.write(self.style.SUCCESS(f'\n✓ Metadata saved to export_metadata.json')) + + # Summary + self.stdout.write('\n' + '=' * 70) + self.stdout.write(self.style.SUCCESS(f'\nSystem Configuration Export Complete!\n')) + self.stdout.write(f' Successful: {len(successful_exports)} exports') + self.stdout.write(f' Failed: {len(failed_exports)} exports') + self.stdout.write(f' Location: {output_dir}\n') + + if failed_exports: + self.stdout.write(self.style.WARNING('\nFailed exports:')) + for name, error in failed_exports: + self.stdout.write(f' - {name}: {error}') + + self.stdout.write('=' * 70 + '\n') + + def get_django_version(self): + import django + return django.get_version() + + def get_database_info(self): + from django.conf import settings + db_config = settings.DATABASES.get('default', {}) + return { + 'engine': db_config.get('ENGINE', '').split('.')[-1], + 'name': db_config.get('NAME', ''), + } diff --git a/backend/igny8_core/auth/migrations/0030_drop_site_blueprints_table.py b/backend/igny8_core/auth/migrations/0030_drop_site_blueprints_table.py new file mode 100644 index 00000000..dc76207a --- /dev/null +++ b/backend/igny8_core/auth/migrations/0030_drop_site_blueprints_table.py @@ -0,0 +1,37 @@ +# Generated manually on 2026-01-13 +# Purpose: Drop legacy igny8_site_blueprints table and related structures + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('igny8_core_auth', '0021_accounttrash_sectortrash_sitetrash'), + ] + + operations = [ + migrations.RunSQL( + sql=""" + -- Drop indexes first + DROP INDEX IF EXISTS igny8_site__account__38f18a_idx; + DROP INDEX IF EXISTS igny8_site__hosting_7a9a3e_idx; + DROP INDEX IF EXISTS igny8_site__hosting_c4bb41_idx; + DROP INDEX IF EXISTS igny8_site__site_id__5f0a4e_idx; + DROP INDEX IF EXISTS igny8_site__site_id_cb1aca_idx; + DROP INDEX IF EXISTS igny8_site__status_247ddc_idx; + DROP INDEX IF EXISTS igny8_site__status_e7ca10_idx; + + -- Drop the table + DROP TABLE IF EXISTS igny8_site_blueprints CASCADE; + + -- Drop the sequence + DROP SEQUENCE IF EXISTS igny8_site_blueprints_id_seq; + """, + reverse_sql=""" + -- Cannot reverse this migration - table structure was removed from models + -- If rollback is needed, restore from database backup + SELECT 1; + """ + ), + ] diff --git a/backend/igny8_core/business/billing/tests/__init__.py b/backend/igny8_core/business/billing/tests/__init__.py deleted file mode 100644 index f42f06a2..00000000 --- a/backend/igny8_core/business/billing/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Billing tests - diff --git a/backend/igny8_core/business/billing/tests/test_concurrency.py b/backend/igny8_core/business/billing/tests/test_concurrency.py deleted file mode 100644 index bb3cf330..00000000 --- a/backend/igny8_core/business/billing/tests/test_concurrency.py +++ /dev/null @@ -1,299 +0,0 @@ -""" -Concurrency tests for payment approval -Tests race conditions and concurrent approval attempts -""" -import pytest -from django.test import TestCase, TransactionTestCase -from django.contrib.auth import get_user_model -from django.db import transaction -from concurrent.futures import ThreadPoolExecutor, as_completed -from decimal import Decimal -from igny8_core.business.billing.models import ( - Invoice, Payment, Subscription, Plan, Account -) -from igny8_core.business.billing.views import approve_payment -from unittest.mock import Mock -import threading - -User = get_user_model() - - -class PaymentApprovalConcurrencyTest(TransactionTestCase): - """Test concurrent payment approval scenarios""" - - def setUp(self): - """Set up test data""" - # Create admin user - self.admin = User.objects.create_user( - email='admin@test.com', - password='testpass123', - is_staff=True - ) - - # Create account - self.account = Account.objects.create( - name='Test Account', - owner=self.admin, - credit_balance=0 - ) - - # Create plan - self.plan = Plan.objects.create( - name='Test Plan', - slug='test-plan', - price=Decimal('100.00'), - currency='USD', - billing_period='monthly', - included_credits=1000 - ) - - # Create subscription - self.subscription = Subscription.objects.create( - account=self.account, - plan=self.plan, - status='pending_payment' - ) - - # Create invoice - self.invoice = Invoice.objects.create( - account=self.account, - invoice_number='INV-TEST-001', - status='pending', - subtotal=Decimal('100.00'), - total_amount=Decimal('100.00'), - currency='USD', - invoice_type='subscription' - ) - - # Create payment - self.payment = Payment.objects.create( - account=self.account, - invoice=self.invoice, - amount=Decimal('100.00'), - currency='USD', - payment_method='bank_transfer', - status='pending_approval', - manual_reference='TEST-REF-001' - ) - - def test_concurrent_approval_attempts(self): - """ - Test that only one concurrent approval succeeds - Multiple admins trying to approve same payment simultaneously - """ - num_threads = 5 - success_count = 0 - failure_count = 0 - results = [] - - def approve_payment_thread(payment_id, admin_user): - """Thread worker to approve payment""" - try: - # Simulate approval logic with transaction - with transaction.atomic(): - payment = Payment.objects.select_for_update().get(id=payment_id) - - # Check if already approved - if payment.status == 'succeeded': - return {'success': False, 'reason': 'already_approved'} - - # Approve payment - payment.status = 'succeeded' - payment.approved_by = admin_user - payment.save() - - # Update invoice - invoice = payment.invoice - invoice.status = 'paid' - invoice.save() - - return {'success': True} - - except Exception as e: - return {'success': False, 'error': str(e)} - - # Create multiple threads attempting approval - with ThreadPoolExecutor(max_workers=num_threads) as executor: - futures = [] - for i in range(num_threads): - future = executor.submit(approve_payment_thread, self.payment.id, self.admin) - futures.append(future) - - # Collect results - for future in as_completed(futures): - result = future.result() - results.append(result) - if result.get('success'): - success_count += 1 - else: - failure_count += 1 - - # Verify only one approval succeeded - self.assertEqual(success_count, 1, "Only one approval should succeed") - self.assertEqual(failure_count, num_threads - 1, "Other attempts should fail") - - # Verify final state - payment = Payment.objects.get(id=self.payment.id) - self.assertEqual(payment.status, 'succeeded') - - invoice = Invoice.objects.get(id=self.invoice.id) - self.assertEqual(invoice.status, 'paid') - - def test_payment_and_invoice_consistency(self): - """ - Test that payment and invoice remain consistent under concurrent operations - """ - def read_payment_invoice(payment_id): - """Read payment and invoice status""" - payment = Payment.objects.get(id=payment_id) - invoice = Invoice.objects.get(id=payment.invoice_id) - return { - 'payment_status': payment.status, - 'invoice_status': invoice.status, - 'consistent': ( - (payment.status == 'succeeded' and invoice.status == 'paid') or - (payment.status == 'pending_approval' and invoice.status == 'pending') - ) - } - - # Approve payment in one thread - def approve(): - with transaction.atomic(): - payment = Payment.objects.select_for_update().get(id=self.payment.id) - payment.status = 'succeeded' - payment.save() - - invoice = Invoice.objects.select_for_update().get(id=self.invoice.id) - invoice.status = 'paid' - invoice.save() - - # Read state in parallel threads - results = [] - with ThreadPoolExecutor(max_workers=10) as executor: - # Start approval - approval_future = executor.submit(approve) - - # Multiple concurrent reads - read_futures = [ - executor.submit(read_payment_invoice, self.payment.id) - for _ in range(20) - ] - - # Wait for approval - approval_future.result() - - # Collect read results - for future in as_completed(read_futures): - results.append(future.result()) - - # All reads should show consistent state - for result in results: - self.assertTrue( - result['consistent'], - f"Inconsistent state: payment={result['payment_status']}, invoice={result['invoice_status']}" - ) - - def test_double_approval_prevention(self): - """ - Test that payment cannot be approved twice - """ - # First approval - with transaction.atomic(): - payment = Payment.objects.select_for_update().get(id=self.payment.id) - payment.status = 'succeeded' - payment.approved_by = self.admin - payment.save() - - invoice = payment.invoice - invoice.status = 'paid' - invoice.save() - - # Attempt second approval - result = None - try: - with transaction.atomic(): - payment = Payment.objects.select_for_update().get(id=self.payment.id) - - # Should detect already approved - if payment.status == 'succeeded': - result = 'already_approved' - else: - payment.status = 'succeeded' - payment.save() - result = 'approved' - except Exception as e: - result = f'error: {str(e)}' - - self.assertEqual(result, 'already_approved', "Second approval should be prevented") - - -class CreditTransactionConcurrencyTest(TransactionTestCase): - """Test concurrent credit additions/deductions""" - - def setUp(self): - self.admin = User.objects.create_user( - email='admin@test.com', - password='testpass123' - ) - self.account = Account.objects.create( - name='Test Account', - owner=self.admin, - credit_balance=1000 - ) - - def test_concurrent_credit_deductions(self): - """ - Test that concurrent credit deductions maintain correct balance - """ - initial_balance = self.account.credit_balance - deduction_amount = 10 - num_operations = 20 - - def deduct_credits(account_id, amount): - """Deduct credits atomically""" - from igny8_core.business.billing.models import CreditTransaction - - with transaction.atomic(): - account = Account.objects.select_for_update().get(id=account_id) - - # Check sufficient balance - if account.credit_balance < amount: - return {'success': False, 'reason': 'insufficient_credits'} - - # Deduct credits - account.credit_balance -= amount - new_balance = account.credit_balance - account.save() - - # Record transaction - CreditTransaction.objects.create( - account=account, - transaction_type='deduction', - amount=-amount, - balance_after=new_balance, - description='Test deduction' - ) - - return {'success': True, 'new_balance': new_balance} - - # Concurrent deductions - with ThreadPoolExecutor(max_workers=10) as executor: - futures = [ - executor.submit(deduct_credits, self.account.id, deduction_amount) - for _ in range(num_operations) - ] - - results = [future.result() for future in as_completed(futures)] - - # Verify all succeeded - success_count = sum(1 for r in results if r.get('success')) - self.assertEqual(success_count, num_operations, "All deductions should succeed") - - # Verify final balance - self.account.refresh_from_db() - expected_balance = initial_balance - (deduction_amount * num_operations) - self.assertEqual( - self.account.credit_balance, - expected_balance, - f"Final balance should be {expected_balance}" - ) diff --git a/backend/igny8_core/business/billing/tests/test_payment_method_filtering.py b/backend/igny8_core/business/billing/tests/test_payment_method_filtering.py deleted file mode 100644 index c1a8216d..00000000 --- a/backend/igny8_core/business/billing/tests/test_payment_method_filtering.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Test payment method filtering by country -""" -from django.test import TestCase, Client -from django.contrib.auth import get_user_model -from igny8_core.business.billing.models import PaymentMethodConfig - -User = get_user_model() - - -class PaymentMethodFilteringTest(TestCase): - """Test payment method filtering by billing country""" - - def setUp(self): - """Create test payment method configs""" - # Global methods (available everywhere) - PaymentMethodConfig.objects.create( - country_code='*', - payment_method='stripe', - display_name='Credit/Debit Card', - is_enabled=True, - sort_order=1, - ) - PaymentMethodConfig.objects.create( - country_code='*', - payment_method='paypal', - display_name='PayPal', - is_enabled=True, - sort_order=2, - ) - - # Country-specific methods - PaymentMethodConfig.objects.create( - country_code='GB', - payment_method='bank_transfer', - display_name='Bank Transfer (UK)', - is_enabled=True, - sort_order=3, - ) - PaymentMethodConfig.objects.create( - country_code='IN', - payment_method='local_wallet', - display_name='UPI/Wallets', - is_enabled=True, - sort_order=4, - ) - PaymentMethodConfig.objects.create( - country_code='PK', - payment_method='bank_transfer', - display_name='Bank Transfer (Pakistan)', - is_enabled=True, - sort_order=5, - ) - - # Disabled method (should not appear) - PaymentMethodConfig.objects.create( - country_code='*', - payment_method='manual', - display_name='Manual', - is_enabled=False, - sort_order=99, - ) - - self.client = Client() - - def test_filter_payment_methods_by_us(self): - """Test filtering for US country - should get only global methods""" - response = self.client.get('/api/v1/billing/admin/payment-methods/?country=US') - - self.assertEqual(response.status_code, 200) - data = response.json() - - self.assertTrue(data['success']) - self.assertEqual(len(data['results']), 2) # Only stripe and paypal - - methods = [m['type'] for m in data['results']] - self.assertIn('stripe', methods) - self.assertIn('paypal', methods) - - def test_filter_payment_methods_by_gb(self): - """Test filtering for GB - should get global + GB-specific""" - response = self.client.get('/api/v1/billing/admin/payment-methods/?country=GB') - - self.assertEqual(response.status_code, 200) - data = response.json() - - self.assertTrue(data['success']) - self.assertEqual(len(data['results']), 3) # stripe, paypal, bank_transfer(GB) - - methods = [m['type'] for m in data['results']] - self.assertIn('stripe', methods) - self.assertIn('paypal', methods) - self.assertIn('bank_transfer', methods) - - def test_filter_payment_methods_by_in(self): - """Test filtering for IN - should get global + IN-specific""" - response = self.client.get('/api/v1/billing/admin/payment-methods/?country=IN') - - self.assertEqual(response.status_code, 200) - data = response.json() - - self.assertTrue(data['success']) - self.assertEqual(len(data['results']), 3) # stripe, paypal, local_wallet(IN) - - methods = [m['type'] for m in data['results']] - self.assertIn('stripe', methods) - self.assertIn('paypal', methods) - self.assertIn('local_wallet', methods) - - def test_disabled_methods_not_returned(self): - """Test that disabled payment methods are not included""" - response = self.client.get('/api/v1/billing/admin/payment-methods/?country=*') - - self.assertEqual(response.status_code, 200) - data = response.json() - - methods = [m['type'] for m in data['results']] - self.assertNotIn('manual', methods) # Disabled method should not appear - - def test_sort_order_respected(self): - \"\"\"Test that payment methods are returned in sort_order\"\"\" - response = self.client.get('/api/v1/billing/admin/payment-methods/?country=GB') - - self.assertEqual(response.status_code, 200) - data = response.json() - - # Verify first method has lowest sort_order - self.assertEqual(data['results'][0]['type'], 'stripe') - self.assertEqual(data['results'][0]['sort_order'], 1) - - def test_default_country_fallback(self): - """Test that missing country parameter defaults to global (*)\"\"\"\n response = self.client.get('/api/v1/billing/admin/payment-methods/') - - self.assertEqual(response.status_code, 200) - data = response.json() - - self.assertTrue(data['success']) - # Should get at least global methods - methods = [m['type'] for m in data['results']] - self.assertIn('stripe', methods) - self.assertIn('paypal', methods) diff --git a/backend/igny8_core/business/billing/tests/test_payment_workflow.py b/backend/igny8_core/business/billing/tests/test_payment_workflow.py deleted file mode 100644 index 1080ac2d..00000000 --- a/backend/igny8_core/business/billing/tests/test_payment_workflow.py +++ /dev/null @@ -1,192 +0,0 @@ -""" -Integration tests for payment workflow -""" -from django.test import TestCase -from django.contrib.auth import get_user_model -from django.utils import timezone -from decimal import Decimal -from datetime import timedelta - -from igny8_core.auth.models import Account, Plan, Subscription -from igny8_core.business.billing.models import Invoice, Payment -from igny8_core.business.billing.services.invoice_service import InvoiceService - -User = get_user_model() - - -class PaymentWorkflowIntegrationTest(TestCase): - """Test complete payment workflow including invoice.subscription FK""" - - def setUp(self): - """Create test data""" - # Create plan - self.plan = Plan.objects.create( - name='Test Plan', - slug='test-plan', - price=Decimal('29.00'), - included_credits=1000, - max_sites=5, - ) - - # Create account - self.account = Account.objects.create( - name='Test Account', - slug='test-account', - status='pending_payment', - billing_country='US', - billing_email='test@example.com', - ) - - # Create user - self.user = User.objects.create_user( - username='testuser', - email='testuser@example.com', - password='testpass123', - account=self.account, - ) - - # Create subscription - billing_period_start = timezone.now() - billing_period_end = billing_period_start + timedelta(days=30) - - self.subscription = Subscription.objects.create( - account=self.account, - plan=self.plan, - status='pending_payment', - current_period_start=billing_period_start, - current_period_end=billing_period_end, - ) - - def test_invoice_subscription_fk_relationship(self): - """Test that invoice.subscription FK works correctly""" - # Create invoice via service - billing_period_start = timezone.now() - billing_period_end = billing_period_start + timedelta(days=30) - - invoice = InvoiceService.create_subscription_invoice( - subscription=self.subscription, - billing_period_start=billing_period_start, - billing_period_end=billing_period_end, - ) - - # Verify FK relationship - self.assertIsNotNone(invoice.subscription) - self.assertEqual(invoice.subscription.id, self.subscription.id) - self.assertEqual(invoice.subscription.plan.id, self.plan.id) - - # Verify can access subscription from invoice - self.assertEqual(invoice.subscription.account, self.account) - self.assertEqual(invoice.subscription.plan.name, 'Test Plan') - - def test_payment_approval_with_subscription(self): - """Test payment approval workflow uses invoice.subscription""" - # Create invoice - billing_period_start = timezone.now() - billing_period_end = billing_period_start + timedelta(days=30) - - invoice = InvoiceService.create_subscription_invoice( - subscription=self.subscription, - billing_period_start=billing_period_start, - billing_period_end=billing_period_end, - ) - - # Create payment - payment = Payment.objects.create( - account=self.account, - invoice=invoice, - amount=invoice.total, - currency='USD', - status='pending_approval', - payment_method='bank_transfer', - manual_reference='TEST-REF-001', - ) - - # Verify payment links to invoice which links to subscription - self.assertIsNotNone(payment.invoice) - self.assertIsNotNone(payment.invoice.subscription) - self.assertEqual(payment.invoice.subscription.id, self.subscription.id) - - # Simulate approval workflow - payment.status = 'succeeded' - payment.approved_by = self.user - payment.approved_at = timezone.now() - payment.save() - - # Update related records - invoice.status = 'paid' - invoice.paid_at = timezone.now() - invoice.save() - - subscription = invoice.subscription - subscription.status = 'active' - subscription.save() - - # Verify workflow completed successfully - self.assertEqual(payment.status, 'succeeded') - self.assertEqual(invoice.status, 'paid') - self.assertEqual(subscription.status, 'active') - self.assertEqual(subscription.plan.included_credits, 1000) - - def test_subscription_dates_not_null_for_paid_plans(self): - """Test that subscription dates are set for paid plans""" - self.assertIsNotNone(self.subscription.current_period_start) - self.assertIsNotNone(self.subscription.current_period_end) - - # Verify dates are in future - self.assertGreater(self.subscription.current_period_end, self.subscription.current_period_start) - - def test_invoice_currency_based_on_country(self): - """Test that invoice currency is set based on billing country""" - # Test US -> USD - self.account.billing_country = 'US' - self.account.save() - - billing_period_start = timezone.now() - billing_period_end = billing_period_start + timedelta(days=30) - - invoice_us = InvoiceService.create_subscription_invoice( - subscription=self.subscription, - billing_period_start=billing_period_start, - billing_period_end=billing_period_end, - ) - self.assertEqual(invoice_us.currency, 'USD') - - # Test GB -> GBP - self.account.billing_country = 'GB' - self.account.save() - - invoice_gb = InvoiceService.create_subscription_invoice( - subscription=self.subscription, - billing_period_start=billing_period_start, - billing_period_end=billing_period_end, - ) - self.assertEqual(invoice_gb.currency, 'GBP') - - # Test IN -> INR - self.account.billing_country = 'IN' - self.account.save() - - invoice_in = InvoiceService.create_subscription_invoice( - subscription=self.subscription, - billing_period_start=billing_period_start, - billing_period_end=billing_period_end, - ) - self.assertEqual(invoice_in.currency, 'INR') - - def test_invoice_due_date_grace_period(self): - """Test that invoice due date uses grace period instead of billing_period_end""" - billing_period_start = timezone.now() - billing_period_end = billing_period_start + timedelta(days=30) - - invoice = InvoiceService.create_subscription_invoice( - subscription=self.subscription, - billing_period_start=billing_period_start, - billing_period_end=billing_period_end, - ) - - # Verify due date is invoice_date + 7 days (grace period) - expected_due_date = invoice.invoice_date + timedelta(days=7) - self.assertEqual(invoice.due_date, expected_due_date) - - # Verify it's NOT billing_period_end - self.assertNotEqual(invoice.due_date, billing_period_end.date()) diff --git a/backend/igny8_core/business/billing/tests/test_phase4_credits.py b/backend/igny8_core/business/billing/tests/test_phase4_credits.py deleted file mode 100644 index b9cfc350..00000000 --- a/backend/igny8_core/business/billing/tests/test_phase4_credits.py +++ /dev/null @@ -1,133 +0,0 @@ -""" -Tests for Phase 4 credit deduction -""" -from unittest.mock import patch -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.billing.services.credit_service import CreditService -from igny8_core.business.billing.constants import CREDIT_COSTS -from igny8_core.business.billing.exceptions import InsufficientCreditsError -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class Phase4CreditTests(IntegrationTestBase): - """Tests for Phase 4 credit deduction""" - - def setUp(self): - super().setUp() - # Set initial credits - self.account.credits = 1000 - self.account.save() - - def test_linking_deducts_correct_credits(self): - """Test that linking deducts correct credits""" - cost = CreditService.get_credit_cost('linking') - expected_cost = CREDIT_COSTS.get('linking', 0) - - self.assertEqual(cost, expected_cost) - self.assertEqual(cost, 8) # From constants - - def test_optimization_deducts_correct_credits(self): - """Test that optimization deducts correct credits based on word count""" - word_count = 500 - cost = CreditService.get_credit_cost('optimization', word_count) - - # Should be 1 credit per 200 words, so 500 words = 3 credits (max(1, 1 * 500/200) = 3) - expected = max(1, int(CREDIT_COSTS.get('optimization', 1) * (word_count / 200))) - self.assertEqual(cost, expected) - - def test_optimization_credits_per_entry_point(self): - """Test that optimization credits are same regardless of entry point""" - word_count = 400 - - # All entry points should use same credit calculation - cost = CreditService.get_credit_cost('optimization', word_count) - - # 400 words = 2 credits (1 * 400/200) - self.assertEqual(cost, 2) - - @patch('igny8_core.business.billing.services.credit_service.CreditService.deduct_credits') - def test_pipeline_deducts_credits_at_each_stage(self, mock_deduct): - """Test that pipeline deducts credits at each stage""" - from igny8_core.business.content.services.content_pipeline_service import ContentPipelineService - from igny8_core.business.linking.services.linker_service import LinkerService - from igny8_core.business.optimization.services.optimizer_service import OptimizerService - - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test", - word_count=400, - source='igny8' - ) - - # Mock the services - with patch.object(LinkerService, 'process') as mock_link, \ - patch.object(OptimizerService, 'optimize_from_writer') as mock_optimize: - - mock_link.return_value = content - mock_optimize.return_value = content - - service = ContentPipelineService() - service.process_writer_content(content.id) - - # Should deduct credits for both linking and optimization - self.assertGreater(mock_deduct.call_count, 0) - - def test_insufficient_credits_blocks_linking(self): - """Test that insufficient credits blocks linking""" - self.account.credits = 5 # Less than linking cost (8) - self.account.save() - - with self.assertRaises(InsufficientCreditsError): - CreditService.check_credits(self.account, 'linking') - - def test_insufficient_credits_blocks_optimization(self): - """Test that insufficient credits blocks optimization""" - self.account.credits = 1 # Less than optimization cost for 500 words - self.account.save() - - with self.assertRaises(InsufficientCreditsError): - CreditService.check_credits(self.account, 'optimization', 500) - - def test_credit_deduction_logged(self): - """Test that credit deduction is logged""" - from igny8_core.business.billing.models import CreditUsageLog - - initial_credits = self.account.credits - cost = CreditService.get_credit_cost('linking') - - CreditService.deduct_credits_for_operation( - account=self.account, - operation_type='linking', - description="Test linking" - ) - - self.account.refresh_from_db() - self.assertEqual(self.account.credits, initial_credits - cost) - - # Check that usage log was created - log = CreditUsageLog.objects.filter( - account=self.account, - operation_type='linking' - ).first() - self.assertIsNotNone(log) - - def test_batch_operations_deduct_multiple_credits(self): - """Test that batch operations deduct multiple credits""" - initial_credits = self.account.credits - linking_cost = CreditService.get_credit_cost('linking') - - # Deduct for 3 linking operations - for i in range(3): - CreditService.deduct_credits_for_operation( - account=self.account, - operation_type='linking', - description=f"Linking {i}" - ) - - self.account.refresh_from_db() - expected_credits = initial_credits - (linking_cost * 3) - self.assertEqual(self.account.credits, expected_credits) - diff --git a/backend/igny8_core/business/content/tests/__init__.py b/backend/igny8_core/business/content/tests/__init__.py deleted file mode 100644 index 974046b9..00000000 --- a/backend/igny8_core/business/content/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Content tests - diff --git a/backend/igny8_core/business/content/tests/test_content_pipeline_service.py b/backend/igny8_core/business/content/tests/test_content_pipeline_service.py deleted file mode 100644 index e88f11da..00000000 --- a/backend/igny8_core/business/content/tests/test_content_pipeline_service.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -Tests for ContentPipelineService -""" -from unittest.mock import patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.content.services.content_pipeline_service import ContentPipelineService -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class ContentPipelineServiceTests(IntegrationTestBase): - """Tests for ContentPipelineService""" - - def setUp(self): - super().setUp() - self.service = ContentPipelineService() - - # Create writer content - self.writer_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Writer Content", - html_content="Writer content.
", - word_count=500, - status='draft', - source='igny8' - ) - - # Create synced content - self.synced_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="WordPress Content", - html_content="WordPress content.
", - word_count=500, - status='draft', - source='wordpress' - ) - - @patch('igny8_core.business.content.services.content_pipeline_service.LinkerService.process') - @patch('igny8_core.business.content.services.content_pipeline_service.OptimizerService.optimize_from_writer') - def test_process_writer_content_full_pipeline(self, mock_optimize, mock_link): - """Test full pipeline for writer content (linking + optimization)""" - mock_link.return_value = self.writer_content - mock_optimize.return_value = self.writer_content - - result = self.service.process_writer_content(self.writer_content.id) - - self.assertEqual(result.id, self.writer_content.id) - mock_link.assert_called_once() - mock_optimize.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.OptimizerService.optimize_from_writer') - def test_process_writer_content_optimization_only(self, mock_optimize): - """Test writer content with optimization only""" - mock_optimize.return_value = self.writer_content - - result = self.service.process_writer_content( - self.writer_content.id, - stages=['optimization'] - ) - - self.assertEqual(result.id, self.writer_content.id) - mock_optimize.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.LinkerService.process') - def test_process_writer_content_linking_only(self, mock_link): - """Test writer content with linking only""" - mock_link.return_value = self.writer_content - - result = self.service.process_writer_content( - self.writer_content.id, - stages=['linking'] - ) - - self.assertEqual(result.id, self.writer_content.id) - mock_link.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.LinkerService.process') - @patch('igny8_core.business.content.services.content_pipeline_service.OptimizerService.optimize_from_writer') - def test_process_writer_content_handles_linker_failure(self, mock_optimize, mock_link): - """Test that pipeline continues when linking fails""" - mock_link.side_effect = Exception("Linking failed") - mock_optimize.return_value = self.writer_content - - # Should not raise exception, should continue to optimization - result = self.service.process_writer_content(self.writer_content.id) - - self.assertEqual(result.id, self.writer_content.id) - mock_optimize.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.OptimizerService.optimize_from_wordpress_sync') - def test_process_synced_content_wordpress(self, mock_optimize): - """Test synced content pipeline for WordPress""" - mock_optimize.return_value = self.synced_content - - result = self.service.process_synced_content(self.synced_content.id) - - self.assertEqual(result.id, self.synced_content.id) - mock_optimize.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.OptimizerService.optimize_from_external_sync') - def test_process_synced_content_shopify(self, mock_optimize): - """Test synced content pipeline for Shopify""" - shopify_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Shopify Content", - word_count=100, - source='shopify' - ) - mock_optimize.return_value = shopify_content - - result = self.service.process_synced_content(shopify_content.id) - - self.assertEqual(result.id, shopify_content.id) - mock_optimize.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.OptimizerService.optimize_manual') - def test_process_synced_content_custom(self, mock_optimize): - """Test synced content pipeline for custom source""" - custom_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Custom Content", - word_count=100, - source='custom' - ) - mock_optimize.return_value = custom_content - - result = self.service.process_synced_content(custom_content.id) - - self.assertEqual(result.id, custom_content.id) - mock_optimize.assert_called_once() - - @patch('igny8_core.business.content.services.content_pipeline_service.ContentPipelineService.process_writer_content') - def test_batch_process_writer_content(self, mock_process): - """Test batch processing writer content""" - content2 = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Content 2", - word_count=100, - source='igny8' - ) - - mock_process.side_effect = [self.writer_content, content2] - - results = self.service.batch_process_writer_content([ - self.writer_content.id, - content2.id - ]) - - self.assertEqual(len(results), 2) - self.assertEqual(mock_process.call_count, 2) - - @patch('igny8_core.business.content.services.content_pipeline_service.ContentPipelineService.process_writer_content') - def test_batch_process_handles_partial_failure(self, mock_process): - """Test batch processing handles partial failures""" - mock_process.side_effect = [self.writer_content, Exception("Failed")] - - results = self.service.batch_process_writer_content([ - self.writer_content.id, - 99999 - ]) - - # Should continue processing and return successful results - self.assertEqual(len(results), 1) - self.assertEqual(results[0].id, self.writer_content.id) - - def test_process_writer_content_invalid_content(self): - """Test that ValueError is raised for invalid content""" - with self.assertRaises(ValueError): - self.service.process_writer_content(99999) - - def test_process_synced_content_invalid_content(self): - """Test that ValueError is raised for invalid synced content""" - with self.assertRaises(ValueError): - self.service.process_synced_content(99999) - diff --git a/backend/igny8_core/business/content/tests/test_universal_content_types.py b/backend/igny8_core/business/content/tests/test_universal_content_types.py deleted file mode 100644 index 12afa558..00000000 --- a/backend/igny8_core/business/content/tests/test_universal_content_types.py +++ /dev/null @@ -1,283 +0,0 @@ -""" -Tests for Universal Content Types (Phase 8) -Tests for product, service, and taxonomy content generation -""" -from unittest.mock import patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.content.services.content_generation_service import ContentGenerationService -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class UniversalContentTypesTests(IntegrationTestBase): - """Tests for Phase 8: Universal Content Types""" - - def setUp(self): - super().setUp() - # Add credits to account for testing - self.account.credits = 10000 - self.account.save() - self.service = ContentGenerationService() - - @patch('igny8_core.ai.tasks.run_ai_task') - def test_product_content_generates_correctly(self, mock_run_ai_task): - """ - Test: Product content generates correctly - Task 17: Verify product generation creates content with correct entity_type and structure - """ - # Mock AI task response - mock_task = MagicMock() - mock_task.id = 'test-task-123' - mock_run_ai_task.delay.return_value = mock_task - - product_data = { - 'name': 'Test Product', - 'description': 'A test product description', - 'features': ['Feature 1', 'Feature 2', 'Feature 3'], - 'target_audience': 'Small businesses', - 'primary_keyword': 'test product', - 'word_count': 1500 - } - - # Generate product content - result = self.service.generate_product_content( - product_data=product_data, - account=self.account, - site=self.site, - sector=self.sector - ) - - # Verify result - self.assertTrue(result.get('success')) - self.assertIsNotNone(result.get('task_id')) - self.assertEqual(result.get('message'), 'Product content generation started') - - # Verify AI task was called with correct function name - mock_run_ai_task.delay.assert_called_once() - call_args = mock_run_ai_task.delay.call_args - self.assertEqual(call_args[1]['function_name'], 'generate_product_content') - self.assertEqual(call_args[1]['payload']['product_name'], 'Test Product') - - @patch('igny8_core.ai.tasks.run_ai_task') - def test_service_pages_work_correctly(self, mock_run_ai_task): - """ - Test: Service pages work correctly - Task 18: Verify service page generation creates content with correct entity_type - """ - # Mock AI task response - mock_task = MagicMock() - mock_task.id = 'test-task-456' - mock_run_ai_task.delay.return_value = mock_task - - service_data = { - 'name': 'Test Service', - 'description': 'A test service description', - 'benefits': ['Benefit 1', 'Benefit 2', 'Benefit 3'], - 'target_audience': 'Enterprise clients', - 'primary_keyword': 'test service', - 'word_count': 1800 - } - - # Generate service page - result = self.service.generate_service_page( - service_data=service_data, - account=self.account, - site=self.site, - sector=self.sector - ) - - # Verify result - self.assertTrue(result.get('success')) - self.assertIsNotNone(result.get('task_id')) - self.assertEqual(result.get('message'), 'Service page generation started') - - # Verify AI task was called with correct function name - mock_run_ai_task.delay.assert_called_once() - call_args = mock_run_ai_task.delay.call_args - self.assertEqual(call_args[1]['function_name'], 'generate_service_page') - self.assertEqual(call_args[1]['payload']['service_name'], 'Test Service') - - @patch('igny8_core.ai.tasks.run_ai_task') - def test_taxonomy_pages_work_correctly(self, mock_run_ai_task): - """ - Test: Taxonomy pages work correctly - Task 19: Verify taxonomy generation creates content with correct entity_type - """ - # Mock AI task response - mock_task = MagicMock() - mock_task.id = 'test-task-789' - mock_run_ai_task.delay.return_value = mock_task - - taxonomy_data = { - 'name': 'Test Taxonomy', - 'description': 'A test taxonomy description', - 'items': ['Category 1', 'Category 2', 'Category 3'], - 'primary_keyword': 'test taxonomy', - 'word_count': 1200 - } - - # Generate taxonomy - result = self.service.generate_taxonomy( - taxonomy_data=taxonomy_data, - account=self.account, - site=self.site, - sector=self.sector - ) - - # Verify result - self.assertTrue(result.get('success')) - self.assertIsNotNone(result.get('task_id')) - self.assertEqual(result.get('message'), 'Taxonomy generation started') - - # Verify AI task was called with correct function name - mock_run_ai_task.delay.assert_called_once() - call_args = mock_run_ai_task.delay.call_args - self.assertEqual(call_args[1]['function_name'], 'generate_taxonomy') - self.assertEqual(call_args[1]['payload']['taxonomy_name'], 'Test Taxonomy') - - def test_product_content_has_correct_structure(self): - """ - Test: Product content generates correctly - Task 17: Verify product content has correct entity_type, json_blocks, and structure_data - """ - # Create product content manually to test structure - product_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Product', - html_content='Product content
', - entity_type='product', - json_blocks=[ - { - 'type': 'product_overview', - 'heading': 'Product Overview', - 'content': 'Product description' - }, - { - 'type': 'features', - 'heading': 'Key Features', - 'items': ['Feature 1', 'Feature 2'] - }, - { - 'type': 'specifications', - 'heading': 'Specifications', - 'data': {'Spec 1': 'Value 1'} - } - ], - structure_data={ - 'product_type': 'software', - 'price_range': '$99-$199', - 'target_market': 'SMB' - }, - word_count=1500, - status='draft' - ) - - # Verify structure - self.assertEqual(product_content.entity_type, 'product') - self.assertIsNotNone(product_content.json_blocks) - self.assertEqual(len(product_content.json_blocks), 3) - self.assertEqual(product_content.json_blocks[0]['type'], 'product_overview') - self.assertIsNotNone(product_content.structure_data) - self.assertEqual(product_content.structure_data['product_type'], 'software') - - def test_service_content_has_correct_structure(self): - """ - Test: Service pages work correctly - Task 18: Verify service content has correct entity_type and json_blocks - """ - # Create service content manually to test structure - service_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Service', - html_content='Service content
', - entity_type='service', - json_blocks=[ - { - 'type': 'service_overview', - 'heading': 'Service Overview', - 'content': 'Service description' - }, - { - 'type': 'benefits', - 'heading': 'Benefits', - 'items': ['Benefit 1', 'Benefit 2'] - }, - { - 'type': 'process', - 'heading': 'Our Process', - 'steps': ['Step 1', 'Step 2'] - } - ], - structure_data={ - 'service_type': 'consulting', - 'duration': '3-6 months', - 'target_market': 'Enterprise' - }, - word_count=1800, - status='draft' - ) - - # Verify structure - self.assertEqual(service_content.entity_type, 'service') - self.assertIsNotNone(service_content.json_blocks) - self.assertEqual(len(service_content.json_blocks), 3) - self.assertEqual(service_content.json_blocks[0]['type'], 'service_overview') - self.assertIsNotNone(service_content.structure_data) - self.assertEqual(service_content.structure_data['service_type'], 'consulting') - - def test_taxonomy_content_has_correct_structure(self): - """ - Test: Taxonomy pages work correctly - Task 19: Verify taxonomy content has correct entity_type and json_blocks - """ - # Create taxonomy content manually to test structure - taxonomy_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Taxonomy', - html_content='Taxonomy content
', - entity_type='taxonomy', - json_blocks=[ - { - 'type': 'taxonomy_overview', - 'heading': 'Taxonomy Overview', - 'content': 'Taxonomy description' - }, - { - 'type': 'categories', - 'heading': 'Categories', - 'items': [ - { - 'name': 'Category 1', - 'description': 'Category description', - 'subcategories': ['Subcat 1', 'Subcat 2'] - } - ] - }, - { - 'type': 'tags', - 'heading': 'Tags', - 'items': ['Tag 1', 'Tag 2', 'Tag 3'] - } - ], - structure_data={ - 'taxonomy_type': 'product_categories', - 'item_count': 10, - 'hierarchy_levels': 3 - }, - word_count=1200, - status='draft' - ) - - # Verify structure - self.assertEqual(taxonomy_content.entity_type, 'taxonomy') - self.assertIsNotNone(taxonomy_content.json_blocks) - self.assertEqual(len(taxonomy_content.json_blocks), 3) - self.assertEqual(taxonomy_content.json_blocks[0]['type'], 'taxonomy_overview') - self.assertIsNotNone(taxonomy_content.structure_data) - self.assertEqual(taxonomy_content.structure_data['taxonomy_type'], 'product_categories') diff --git a/backend/igny8_core/business/integration/services/content_sync_service.py b/backend/igny8_core/business/integration/services/content_sync_service.py index 7affeef0..9f5ccffa 100644 --- a/backend/igny8_core/business/integration/services/content_sync_service.py +++ b/backend/igny8_core/business/integration/services/content_sync_service.py @@ -374,100 +374,10 @@ class ContentSyncService: Returns: dict: Sync result with synced_count """ - try: - from igny8_core.business.site_building.models import SiteBlueprint - from igny8_core.business.site_building.services.taxonomy_service import TaxonomyService - - # Get or create site blueprint for this site - blueprint = SiteBlueprint.objects.filter( - account=integration.account, - site=integration.site - ).first() - - if not blueprint: - logger.warning(f"No blueprint found for site {integration.site.id}, skipping taxonomy sync") - return {'success': True, 'synced_count': 0} - - taxonomy_service = TaxonomyService() - synced_count = 0 - - # Sync WordPress categories - categories = client.get_categories(per_page=100) - category_records = [ - { - 'name': cat['name'], - 'slug': cat['slug'], - 'description': cat.get('description', ''), - 'taxonomy_type': 'blog_category', - 'external_reference': str(cat['id']), - 'metadata': {'parent': cat.get('parent', 0)} - } - for cat in categories - ] - if category_records: - taxonomy_service.import_from_external( - blueprint, - category_records, - default_type='blog_category' - ) - synced_count += len(category_records) - - # Sync WordPress tags - tags = client.get_tags(per_page=100) - tag_records = [ - { - 'name': tag['name'], - 'slug': tag['slug'], - 'description': tag.get('description', ''), - 'taxonomy_type': 'blog_tag', - 'external_reference': str(tag['id']) - } - for tag in tags - ] - if tag_records: - taxonomy_service.import_from_external( - blueprint, - tag_records, - default_type='blog_tag' - ) - synced_count += len(tag_records) - - # Sync WooCommerce product categories if available (401 is expected if WooCommerce not installed or credentials missing) - try: - product_categories = client.get_product_categories(per_page=100) - product_category_records = [ - { - 'name': cat['name'], - 'slug': cat['slug'], - 'description': cat.get('description', ''), - 'taxonomy_type': 'product_category', - 'external_reference': f"wc_cat_{cat['id']}", - 'metadata': {'parent': cat.get('parent', 0)} - } - for cat in product_categories - ] - if product_category_records: - taxonomy_service.import_from_external( - blueprint, - product_category_records, - default_type='product_category' - ) - synced_count += len(product_category_records) - except Exception as e: - # Silently skip WooCommerce if not available (401 means no consumer key/secret configured or plugin not installed) - logger.debug(f"WooCommerce product categories not available: {e}") - - return { - 'success': True, - 'synced_count': synced_count - } - except Exception as e: - logger.error(f"Error syncing taxonomies from WordPress: {e}", exc_info=True) - return { - 'success': False, - 'error': str(e), - 'synced_count': 0 - } + # REMOVED: Legacy SiteBlueprint taxonomy sync removed. + # Taxonomy management now uses ContentTaxonomy model. + logger.info(f"Skipping legacy taxonomy sync for site {integration.site.id}") + return {'success': True, 'synced_count': 0} def _sync_taxonomies_to_wordpress( self, diff --git a/backend/igny8_core/business/integration/services/sync_health_service.py b/backend/igny8_core/business/integration/services/sync_health_service.py index 450b04a6..d6f9a67e 100644 --- a/backend/igny8_core/business/integration/services/sync_health_service.py +++ b/backend/igny8_core/business/integration/services/sync_health_service.py @@ -308,11 +308,14 @@ class SyncHealthService: """ Detect mismatches between IGNY8 and WordPress. + DEPRECATED: Legacy SiteBlueprint taxonomy mismatch detection removed. + Taxonomy management now uses ContentTaxonomy model. + Args: integration: SiteIntegration instance Returns: - dict: Mismatch details + dict: Mismatch details (empty for now) """ mismatches = { 'taxonomies': { @@ -330,116 +333,8 @@ class SyncHealthService: } } - try: - from igny8_core.utils.wordpress import WordPressClient - from igny8_core.business.site_building.models import SiteBlueprint, SiteBlueprintTaxonomy - from igny8_core.business.content.models import Content - - credentials = integration.get_credentials() - client = WordPressClient( - site_url=integration.config_json.get('site_url', ''), - username=credentials.get('username'), - app_password=credentials.get('app_password') - ) - - # Get site blueprint - blueprint = SiteBlueprint.objects.filter( - account=integration.account, - site=integration.site - ).first() - - if not blueprint: - return mismatches - - # Check taxonomy mismatches - # Get IGNY8 taxonomies - igny8_taxonomies = SiteBlueprintTaxonomy.objects.filter( - site_blueprint=blueprint - ) - - # Get WordPress categories - wp_categories = client.get_categories(per_page=100) - wp_category_ids = {str(cat['id']): cat for cat in wp_categories} - - # Get WordPress tags - wp_tags = client.get_tags(per_page=100) - wp_tag_ids = {str(tag['id']): tag for tag in wp_tags} - - for taxonomy in igny8_taxonomies: - if taxonomy.external_reference: - # Check if still exists in WordPress - if taxonomy.taxonomy_type in ['blog_category', 'product_category']: - if taxonomy.external_reference not in wp_category_ids: - mismatches['taxonomies']['missing_in_wordpress'].append({ - 'id': taxonomy.id, - 'name': taxonomy.name, - 'type': taxonomy.taxonomy_type, - 'external_reference': taxonomy.external_reference - }) - elif taxonomy.taxonomy_type in ['blog_tag', 'product_tag']: - if taxonomy.external_reference not in wp_tag_ids: - mismatches['taxonomies']['missing_in_wordpress'].append({ - 'id': taxonomy.id, - 'name': taxonomy.name, - 'type': taxonomy.taxonomy_type, - 'external_reference': taxonomy.external_reference - }) - else: - # Taxonomy exists in IGNY8 but not synced to WordPress - mismatches['taxonomies']['missing_in_wordpress'].append({ - 'id': taxonomy.id, - 'name': taxonomy.name, - 'type': taxonomy.taxonomy_type - }) - - # Check for WordPress taxonomies not in IGNY8 - for cat in wp_categories: - if not SiteBlueprintTaxonomy.objects.filter( - site_blueprint=blueprint, - external_reference=str(cat['id']) - ).exists(): - mismatches['taxonomies']['missing_in_igny8'].append({ - 'name': cat['name'], - 'slug': cat['slug'], - 'type': 'blog_category', - 'external_reference': str(cat['id']) - }) - - for tag in wp_tags: - if not SiteBlueprintTaxonomy.objects.filter( - site_blueprint=blueprint, - external_reference=str(tag['id']) - ).exists(): - mismatches['taxonomies']['missing_in_igny8'].append({ - 'name': tag['name'], - 'slug': tag['slug'], - 'type': 'blog_tag', - 'external_reference': str(tag['id']) - }) - - # Check content mismatches (basic check) - igny8_content = Content.objects.filter( - account=integration.account, - site=integration.site, - source='igny8', - status='publish' - ) - - for content in igny8_content[:50]: # Limit check - if content.metadata and content.metadata.get('wordpress_id'): - # Content should exist in WordPress (would need to check) - # For now, just note if metadata exists - pass - else: - # Content not synced to WordPress - mismatches['posts']['missing_in_wordpress'].append({ - 'id': content.id, - 'title': content.title, - 'type': content.content_type - }) - - except Exception as e: - logger.warning(f"Error detecting WordPress mismatches: {e}") + # Legacy taxonomy detection removed - would need to be reimplemented with ContentTaxonomy + logger.info(f"Mismatch detection for integration {integration.id} - legacy code removed") return mismatches diff --git a/backend/igny8_core/business/integration/tests/__init__.py b/backend/igny8_core/business/integration/tests/__init__.py deleted file mode 100644 index 7c4954d2..00000000 --- a/backend/igny8_core/business/integration/tests/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -Integration Tests -Phase 6: Site Integration & Multi-Destination Publishing -""" - diff --git a/backend/igny8_core/business/integration/tests/test_content_sync.py b/backend/igny8_core/business/integration/tests/test_content_sync.py deleted file mode 100644 index 3e406878..00000000 --- a/backend/igny8_core/business/integration/tests/test_content_sync.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Tests for ContentSyncService -Phase 6: Site Integration & Multi-Destination Publishing -""" -from django.test import TestCase -from unittest.mock import patch, Mock - -from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector -from igny8_core.business.integration.models import SiteIntegration -from igny8_core.business.integration.services.content_sync_service import ContentSyncService -from igny8_core.business.content.models import Content - - -class ContentSyncServiceTestCase(TestCase): - """Test cases for ContentSyncService""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - self.site = Site.objects.create( - account=self.account, - name="Test Site", - slug="test-site", - industry=self.industry - ) - self.sector = Sector.objects.create( - account=self.account, - site=self.site, - industry_sector=self.industry_sector, - name="Test Sector", - slug="test-sector" - ) - self.integration = SiteIntegration.objects.create( - account=self.account, - site=self.site, - platform='wordpress', - platform_type='cms', - sync_enabled=True - ) - self.service = ContentSyncService() - - def test_sync_content_from_wordpress_creates_content(self): - """Test: WordPress sync works (when plugin connected)""" - mock_posts = [ - { - 'id': 1, - 'title': 'Test Post', - 'content': 'Test content
', - 'status': 'publish', - } - ] - - with patch.object(self.service, '_fetch_wordpress_posts') as mock_fetch: - mock_fetch.return_value = mock_posts - - result = self.service.sync_from_wordpress(self.integration) - - self.assertTrue(result.get('success')) - self.assertEqual(result.get('synced_count'), 1) - - # Verify content was created - content = Content.objects.filter(site=self.site).first() - self.assertIsNotNone(content) - self.assertEqual(content.title, 'Test Post') - self.assertEqual(content.source, 'wordpress') - - def test_sync_content_from_shopify_creates_content(self): - """Test: Content sync works""" - mock_products = [ - { - 'id': 1, - 'title': 'Test Product', - 'body_html': 'Product description
', - } - ] - - with patch.object(self.service, '_fetch_shopify_products') as mock_fetch: - mock_fetch.return_value = mock_products - - result = self.service.sync_from_shopify(self.integration) - - self.assertTrue(result.get('success')) - self.assertEqual(result.get('synced_count'), 1) - - def test_sync_handles_duplicate_content(self): - """Test: Content sync works""" - # Create existing content - Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Post", - html_content="Existing
", - source='wordpress' - ) - - mock_posts = [ - { - 'id': 1, - 'title': 'Test Post', - 'content': 'Updated content
', - } - ] - - with patch.object(self.service, '_fetch_wordpress_posts') as mock_fetch: - mock_fetch.return_value = mock_posts - - result = self.service.sync_from_wordpress(self.integration) - - # Should update existing, not create duplicate - content_count = Content.objects.filter( - site=self.site, - title='Test Post' - ).count() - self.assertEqual(content_count, 1) - diff --git a/backend/igny8_core/business/integration/tests/test_integration_service.py b/backend/igny8_core/business/integration/tests/test_integration_service.py deleted file mode 100644 index 0b700f64..00000000 --- a/backend/igny8_core/business/integration/tests/test_integration_service.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Tests for IntegrationService -Phase 6: Site Integration & Multi-Destination Publishing -""" -from django.test import TestCase - -from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector -from igny8_core.business.integration.models import SiteIntegration -from igny8_core.business.integration.services.integration_service import IntegrationService - - -class IntegrationServiceTestCase(TestCase): - """Test cases for IntegrationService""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - self.site = Site.objects.create( - account=self.account, - name="Test Site", - slug="test-site", - industry=self.industry - ) - self.sector = Sector.objects.create( - account=self.account, - site=self.site, - industry_sector=self.industry_sector, - name="Test Sector", - slug="test-sector" - ) - self.service = IntegrationService() - - def test_create_integration_stores_config(self): - """Test: Site integrations work correctly""" - integration = self.service.create_integration( - site=self.site, - platform='wordpress', - config={'url': 'https://example.com'}, - credentials={'api_key': 'test-key'}, - platform_type='cms' - ) - - self.assertIsNotNone(integration) - self.assertEqual(integration.platform, 'wordpress') - self.assertEqual(integration.platform_type, 'cms') - self.assertEqual(integration.config_json.get('url'), 'https://example.com') - self.assertTrue(integration.is_active) - - def test_get_integrations_for_site_returns_all(self): - """Test: Site integrations work correctly""" - self.service.create_integration( - site=self.site, - platform='wordpress', - config={}, - credentials={} - ) - self.service.create_integration( - site=self.site, - platform='shopify', - config={}, - credentials={} - ) - - integrations = self.service.get_integrations_for_site(self.site) - - self.assertEqual(integrations.count(), 2) - platforms = [i.platform for i in integrations] - self.assertIn('wordpress', platforms) - self.assertIn('shopify', platforms) - - def test_test_connection_validates_credentials(self): - """Test: Site integrations work correctly""" - # Test with unsupported platform to verify NotImplementedError is raised - integration = self.service.create_integration( - site=self.site, - platform='unsupported_platform', - config={'url': 'https://example.com'}, - credentials={'api_key': 'test-key'} - ) - - with self.assertRaises(NotImplementedError): - # Connection testing should raise NotImplementedError for unsupported platforms - self.service.test_connection(integration) - - def test_update_integration_updates_fields(self): - """Test: Site integrations work correctly""" - integration = self.service.create_integration( - site=self.site, - platform='wordpress', - config={'url': 'https://old.com'}, - credentials={} - ) - - updated = self.service.update_integration( - integration, - config={'url': 'https://new.com'}, - is_active=False - ) - - self.assertEqual(updated.config_json.get('url'), 'https://new.com') - self.assertFalse(updated.is_active) - diff --git a/backend/igny8_core/business/integration/tests/test_sync_service.py b/backend/igny8_core/business/integration/tests/test_sync_service.py deleted file mode 100644 index b78f870e..00000000 --- a/backend/igny8_core/business/integration/tests/test_sync_service.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Tests for SyncService -Phase 6: Site Integration & Multi-Destination Publishing -""" -from django.test import TestCase -from django.utils import timezone -from unittest.mock import patch, Mock - -from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector -from igny8_core.business.integration.models import SiteIntegration -from igny8_core.business.integration.services.sync_service import SyncService - - -class SyncServiceTestCase(TestCase): - """Test cases for SyncService""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - self.site = Site.objects.create( - account=self.account, - name="Test Site", - slug="test-site", - industry=self.industry - ) - self.sector = Sector.objects.create( - account=self.account, - site=self.site, - industry_sector=self.industry_sector, - name="Test Sector", - slug="test-sector" - ) - self.integration = SiteIntegration.objects.create( - account=self.account, - site=self.site, - platform='wordpress', - platform_type='cms', - sync_enabled=True, - sync_status='pending' - ) - self.service = SyncService() - - def test_sync_updates_status(self): - """Test: Two-way sync functions properly""" - with patch.object(self.service, '_sync_to_external') as mock_sync_to, \ - patch.object(self.service, '_sync_from_external') as mock_sync_from: - mock_sync_to.return_value = {'success': True, 'synced': 5} - mock_sync_from.return_value = {'success': True, 'synced': 3} - - result = self.service.sync(self.integration, direction='both') - - self.assertTrue(result.get('success')) - self.integration.refresh_from_db() - self.assertEqual(self.integration.sync_status, 'success') - self.assertIsNotNone(self.integration.last_sync_at) - - def test_sync_to_external_only(self): - """Test: Two-way sync functions properly""" - with patch.object(self.service, '_sync_to_external') as mock_sync_to: - mock_sync_to.return_value = {'success': True, 'synced': 5} - - result = self.service.sync(self.integration, direction='to_external') - - self.assertTrue(result.get('success')) - mock_sync_to.assert_called_once() - - def test_sync_from_external_only(self): - """Test: WordPress sync works (when plugin connected)""" - with patch.object(self.service, '_sync_from_external') as mock_sync_from: - mock_sync_from.return_value = {'success': True, 'synced': 3} - - result = self.service.sync(self.integration, direction='from_external') - - self.assertTrue(result.get('success')) - mock_sync_from.assert_called_once() - - def test_sync_handles_errors(self): - """Test: Two-way sync functions properly""" - with patch.object(self.service, '_sync_to_external') as mock_sync_to: - mock_sync_to.side_effect = Exception("Sync failed") - - result = self.service.sync(self.integration, direction='to_external') - - self.assertFalse(result.get('success')) - self.integration.refresh_from_db() - self.assertEqual(self.integration.sync_status, 'failed') - self.assertIsNotNone(self.integration.sync_error) - diff --git a/backend/igny8_core/business/linking/tests/__init__.py b/backend/igny8_core/business/linking/tests/__init__.py deleted file mode 100644 index 39bf3d5f..00000000 --- a/backend/igny8_core/business/linking/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Linking tests - diff --git a/backend/igny8_core/business/linking/tests/test_candidate_engine.py b/backend/igny8_core/business/linking/tests/test_candidate_engine.py deleted file mode 100644 index a3de8f1f..00000000 --- a/backend/igny8_core/business/linking/tests/test_candidate_engine.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -Tests for CandidateEngine -""" -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.linking.services.candidate_engine import CandidateEngine -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class CandidateEngineTests(IntegrationTestBase): - """Tests for CandidateEngine""" - - def setUp(self): - super().setUp() - self.engine = CandidateEngine() - - # Create source content - self.source_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Source Content", - html_content="Source content about test keyword.
", - primary_keyword="test keyword", - secondary_keywords=["keyword1", "keyword2"], - categories=["category1"], - tags=["tag1", "tag2"], - word_count=100, - status='draft' - ) - - # Create relevant content (same keyword) - self.relevant_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Relevant Content", - html_content="Relevant content about test keyword.
", - primary_keyword="test keyword", - secondary_keywords=["keyword1"], - categories=["category1"], - tags=["tag1"], - word_count=150, - status='draft' - ) - - # Create less relevant content (different keyword) - self.less_relevant = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Less Relevant", - html_content="Different content.
", - primary_keyword="different keyword", - word_count=100, - status='draft' - ) - - def test_find_candidates_returns_relevant_content(self): - """Test that find_candidates returns relevant content""" - candidates = self.engine.find_candidates(self.source_content, max_candidates=10) - - # Should find relevant content - candidate_ids = [c['content_id'] for c in candidates] - self.assertIn(self.relevant_content.id, candidate_ids) - - def test_find_candidates_scores_by_relevance(self): - """Test that candidates are scored by relevance""" - candidates = self.engine.find_candidates(self.source_content, max_candidates=10) - - # Relevant content should have higher score - relevant_candidate = next((c for c in candidates if c['content_id'] == self.relevant_content.id), None) - self.assertIsNotNone(relevant_candidate) - self.assertGreater(relevant_candidate['relevance_score'], 0) - - def test_find_candidates_excludes_self(self): - """Test that source content is excluded from candidates""" - candidates = self.engine.find_candidates(self.source_content, max_candidates=10) - - candidate_ids = [c['content_id'] for c in candidates] - self.assertNotIn(self.source_content.id, candidate_ids) - - def test_find_candidates_respects_account_isolation(self): - """Test that candidates are only from same account""" - # Create content from different account - from igny8_core.auth.models import Account - other_account = Account.objects.create( - name="Other Account", - slug="other-account", - plan=self.plan, - owner=self.user - ) - - other_content = Content.objects.create( - account=other_account, - site=self.site, - sector=self.sector, - title="Other Account Content", - primary_keyword="test keyword", - word_count=100, - status='draft' - ) - - candidates = self.engine.find_candidates(self.source_content, max_candidates=10) - candidate_ids = [c['content_id'] for c in candidates] - self.assertNotIn(other_content.id, candidate_ids) - - def test_find_candidates_returns_empty_for_no_content(self): - """Test that empty list is returned when no content""" - empty_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Empty", - html_content="", - word_count=0, - status='draft' - ) - - candidates = self.engine.find_candidates(empty_content, max_candidates=10) - self.assertEqual(len(candidates), 0) - - def test_find_candidates_respects_max_candidates(self): - """Test that max_candidates limit is respected""" - # Create multiple relevant content items - for i in range(15): - Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title=f"Content {i}", - primary_keyword="test keyword", - word_count=100, - status='draft' - ) - - candidates = self.engine.find_candidates(self.source_content, max_candidates=5) - self.assertLessEqual(len(candidates), 5) - diff --git a/backend/igny8_core/business/linking/tests/test_injection_engine.py b/backend/igny8_core/business/linking/tests/test_injection_engine.py deleted file mode 100644 index fe5488f8..00000000 --- a/backend/igny8_core/business/linking/tests/test_injection_engine.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -Tests for InjectionEngine -""" -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.linking.services.injection_engine import InjectionEngine -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class InjectionEngineTests(IntegrationTestBase): - """Tests for InjectionEngine""" - - def setUp(self): - super().setUp() - self.engine = InjectionEngine() - - # Create content with HTML - self.content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="This is test content with some keywords and text.
", - word_count=100, - status='draft' - ) - - def test_inject_links_adds_links_to_html(self): - """Test that links are injected into HTML content""" - candidates = [{ - 'content_id': 1, - 'title': 'Target Content', - 'url': '/content/1/', - 'relevance_score': 50, - 'anchor_text': 'keywords' - }] - - result = self.engine.inject_links(self.content, candidates, max_links=5) - - # Check that link was added - self.assertIn('keywords', result['html_content']) - self.assertEqual(result['links_added'], 1) - self.assertEqual(len(result['links']), 1) - - def test_inject_links_respects_max_links(self): - """Test that max_links limit is respected""" - candidates = [ - {'content_id': i, 'title': f'Content {i}', 'url': f'/content/{i}/', - 'relevance_score': 50, 'anchor_text': f'keyword{i}'} - for i in range(10) - ] - - # Update HTML to include all anchor texts - self.content.html_content = "" + " ".join([f'keyword{i}' for i in range(10)]) + "
" - self.content.save() - - result = self.engine.inject_links(self.content, candidates, max_links=3) - - self.assertLessEqual(result['links_added'], 3) - self.assertLessEqual(len(result['links']), 3) - - def test_inject_links_returns_unchanged_when_no_candidates(self): - """Test that content is unchanged when no candidates""" - original_html = self.content.html_content - - result = self.engine.inject_links(self.content, [], max_links=5) - - self.assertEqual(result['html_content'], original_html) - self.assertEqual(result['links_added'], 0) - self.assertEqual(len(result['links']), 0) - - def test_inject_links_returns_unchanged_when_no_html(self): - """Test that empty HTML returns unchanged""" - self.content.html_content = "" - self.content.save() - - candidates = [{ - 'content_id': 1, - 'title': 'Target', - 'url': '/content/1/', - 'relevance_score': 50, - 'anchor_text': 'test' - }] - - result = self.engine.inject_links(self.content, candidates, max_links=5) - - self.assertEqual(result['html_content'], "") - self.assertEqual(result['links_added'], 0) - - def test_inject_links_case_insensitive_matching(self): - """Test that anchor text matching is case-insensitive""" - self.content.html_content = "This is TEST content.
" - self.content.save() - - candidates = [{ - 'content_id': 1, - 'title': 'Target', - 'url': '/content/1/', - 'relevance_score': 50, - 'anchor_text': 'test' - }] - - result = self.engine.inject_links(self.content, candidates, max_links=5) - - # Should find and replace despite case difference - self.assertIn('internal-link', result['html_content']) - self.assertEqual(result['links_added'], 1) - - def test_inject_links_prevents_duplicate_links(self): - """Test that same candidate is not linked twice""" - candidates = [ - { - 'content_id': 1, - 'title': 'Target', - 'url': '/content/1/', - 'relevance_score': 50, - 'anchor_text': 'test' - }, - { - 'content_id': 1, # Same content_id - 'title': 'Target', - 'url': '/content/1/', - 'relevance_score': 40, - 'anchor_text': 'test' - } - ] - - self.content.html_content = "This is test content with test keywords.
" - self.content.save() - - result = self.engine.inject_links(self.content, candidates, max_links=5) - - # Should only add one link despite two candidates - self.assertEqual(result['links_added'], 1) - self.assertEqual(result['html_content'].count('internal-link'), 1) - diff --git a/backend/igny8_core/business/linking/tests/test_linker_service.py b/backend/igny8_core/business/linking/tests/test_linker_service.py deleted file mode 100644 index 653c4ec6..00000000 --- a/backend/igny8_core/business/linking/tests/test_linker_service.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -Tests for LinkerService -""" -from unittest.mock import Mock, patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.linking.services.linker_service import LinkerService -from igny8_core.business.billing.exceptions import InsufficientCreditsError -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class LinkerServiceTests(IntegrationTestBase): - """Tests for LinkerService""" - - def setUp(self): - super().setUp() - self.service = LinkerService() - - # Create test content - self.content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="This is test content with some keywords.
", - primary_keyword="test keyword", - word_count=100, - status='draft' - ) - - # Create another content for linking - self.target_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Target Content", - html_content="Target content for linking.
", - primary_keyword="test keyword", - word_count=150, - status='draft' - ) - - @patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits') - @patch('igny8_core.business.linking.services.linker_service.CandidateEngine.find_candidates') - @patch('igny8_core.business.linking.services.linker_service.InjectionEngine.inject_links') - @patch('igny8_core.business.linking.services.linker_service.CreditService.deduct_credits_for_operation') - def test_process_single_content(self, mock_deduct, mock_inject, mock_find, mock_check): - """Test processing single content for linking""" - # Setup mocks - mock_check.return_value = True - mock_find.return_value = [{ - 'content_id': self.target_content.id, - 'title': 'Target Content', - 'url': '/content/2/', - 'relevance_score': 50, - 'anchor_text': 'test keyword' - }] - mock_inject.return_value = { - 'html_content': 'This is test content with test keyword.
', - 'links': [{ - 'content_id': self.target_content.id, - 'anchor_text': 'test keyword', - 'url': '/content/2/' - }], - 'links_added': 1 - } - - # Execute - result = self.service.process(self.content.id) - - # Assertions - self.assertEqual(result.id, self.content.id) - self.assertEqual(result.linker_version, 1) - self.assertEqual(len(result.internal_links), 1) - mock_check.assert_called_once_with(self.account, 'linking') - mock_deduct.assert_called_once() - - @patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits') - def test_process_insufficient_credits(self, mock_check): - """Test that InsufficientCreditsError is raised when credits are insufficient""" - mock_check.side_effect = InsufficientCreditsError("Insufficient credits") - - with self.assertRaises(InsufficientCreditsError): - self.service.process(self.content.id) - - def test_process_content_not_found(self): - """Test that ValueError is raised when content doesn't exist""" - with self.assertRaises(ValueError): - self.service.process(99999) - - @patch('igny8_core.business.linking.services.linker_service.LinkerService.process') - def test_batch_process_multiple_content(self, mock_process): - """Test batch processing multiple content items""" - # Create additional content - content2 = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Content 2", - html_content="Content 2
", - word_count=100, - status='draft' - ) - - # Setup mock - mock_process.side_effect = [self.content, content2] - - # Execute - results = self.service.batch_process([self.content.id, content2.id]) - - # Assertions - self.assertEqual(len(results), 2) - self.assertEqual(mock_process.call_count, 2) - - @patch('igny8_core.business.linking.services.linker_service.LinkerService.process') - def test_batch_process_handles_partial_failure(self, mock_process): - """Test batch processing handles partial failures""" - # Setup mock to fail on second item - mock_process.side_effect = [self.content, Exception("Processing failed")] - - # Execute - results = self.service.batch_process([self.content.id, 99999]) - - # Assertions - should continue processing other items - self.assertEqual(len(results), 1) - self.assertEqual(results[0].id, self.content.id) - - @patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits') - @patch('igny8_core.business.linking.services.linker_service.CandidateEngine.find_candidates') - def test_process_no_candidates_found(self, mock_find, mock_check): - """Test processing when no candidates are found""" - mock_check.return_value = True - mock_find.return_value = [] - - # Execute - result = self.service.process(self.content.id) - - # Assertions - should return content unchanged - self.assertEqual(result.id, self.content.id) - self.assertEqual(result.linker_version, 0) # Not incremented - diff --git a/backend/igny8_core/business/linking/tests/test_universal_content_linking.py b/backend/igny8_core/business/linking/tests/test_universal_content_linking.py deleted file mode 100644 index 38b2cd67..00000000 --- a/backend/igny8_core/business/linking/tests/test_universal_content_linking.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Tests for Universal Content Types Linking (Phase 8) -Tests for product and taxonomy linking -""" -from unittest.mock import patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.linking.services.linker_service import LinkerService -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class UniversalContentLinkingTests(IntegrationTestBase): - """Tests for Phase 8: Universal Content Types Linking""" - - def setUp(self): - super().setUp() - # Add credits to account for testing - self.account.credits = 10000 - self.account.save() - self.linker_service = LinkerService() - - # Create product content - self.product_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Product', - html_content='Product content with features and specifications.
', - entity_type='product', - json_blocks=[ - {'type': 'features', 'heading': 'Features', 'items': ['Feature 1', 'Feature 2']} - ], - structure_data={'product_type': 'software'}, - word_count=1500, - status='draft' - ) - - # Create related product - self.related_product = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Related Product', - html_content='Related product content.
', - entity_type='product', - structure_data={'product_type': 'software'}, - word_count=1500, - status='draft' - ) - - # Create service content - self.service_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Related Service', - html_content='Service content.
', - entity_type='service', - word_count=1800, - status='draft' - ) - - # Create taxonomy content - self.taxonomy_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Taxonomy', - html_content='Taxonomy content with categories.
', - entity_type='taxonomy', - json_blocks=[ - { - 'type': 'categories', - 'heading': 'Categories', - 'items': [ - {'name': 'Category 1', 'description': 'Desc 1', 'subcategories': []} - ] - } - ], - word_count=1200, - status='draft' - ) - - # Create related taxonomy - self.related_taxonomy = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Related Taxonomy', - html_content='Related taxonomy content.
', - entity_type='taxonomy', - word_count=1200, - status='draft' - ) - - @patch('igny8_core.business.linking.services.linker_service.InjectionEngine.inject_links') - @patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits') - @patch('igny8_core.business.linking.services.linker_service.CreditService.deduct_credits_for_operation') - def test_linking_works_for_products(self, mock_deduct, mock_check_credits, mock_inject_links): - """ - Test: Linking works for all content types (products, taxonomies) - Task 20: Verify product linking finds related products and services - """ - # Mock injection engine - mock_inject_links.return_value = { - 'html_content': 'Product content with links.
', - 'links': [ - {'content_id': self.related_product.id, 'anchor_text': 'Related Product'}, - {'content_id': self.service_content.id, 'anchor_text': 'Related Service'} - ], - 'links_added': 2 - } - - # Process product linking - result = self.linker_service.process_product(self.product_content.id) - - # Verify result - self.assertIsNotNone(result) - self.assertEqual(result.entity_type, 'product') - self.assertIsNotNone(result.internal_links) - self.assertEqual(len(result.internal_links), 2) - self.assertEqual(result.linker_version, 1) - - # Verify injection was called - mock_inject_links.assert_called_once() - candidates = mock_inject_links.call_args[0][1] - self.assertGreater(len(candidates), 0) - - # Verify product candidates were found - product_candidates = [c for c in candidates if c.get('content_id') == self.related_product.id] - self.assertGreater(len(product_candidates), 0) - - @patch('igny8_core.business.linking.services.linker_service.InjectionEngine.inject_links') - @patch('igny8_core.business.linking.services.linker_service.CreditService.check_credits') - @patch('igny8_core.business.linking.services.linker_service.CreditService.deduct_credits_for_operation') - def test_linking_works_for_taxonomies(self, mock_deduct, mock_check_credits, mock_inject_links): - """ - Test: Linking works for all content types (products, taxonomies) - Task 20: Verify taxonomy linking finds related taxonomies and content - """ - # Mock injection engine - mock_inject_links.return_value = { - 'html_content': 'Taxonomy content with links.
', - 'links': [ - {'content_id': self.related_taxonomy.id, 'anchor_text': 'Related Taxonomy'} - ], - 'links_added': 1 - } - - # Process taxonomy linking - result = self.linker_service.process_taxonomy(self.taxonomy_content.id) - - # Verify result - self.assertIsNotNone(result) - self.assertEqual(result.entity_type, 'taxonomy') - self.assertIsNotNone(result.internal_links) - self.assertEqual(len(result.internal_links), 1) - self.assertEqual(result.linker_version, 1) - - # Verify injection was called - mock_inject_links.assert_called_once() - candidates = mock_inject_links.call_args[0][1] - self.assertGreater(len(candidates), 0) - - # Verify taxonomy candidates were found - taxonomy_candidates = [c for c in candidates if c.get('content_id') == self.related_taxonomy.id] - self.assertGreater(len(taxonomy_candidates), 0) - - def test_product_linking_finds_related_products(self): - """ - Test: Linking works for all content types (products, taxonomies) - Task 20: Verify _find_product_candidates finds related products - """ - candidates = self.linker_service._find_product_candidates(self.product_content) - - # Should find related product - product_ids = [c['content_id'] for c in candidates] - self.assertIn(self.related_product.id, product_ids) - - # Should find related service - self.assertIn(self.service_content.id, product_ids) - - def test_taxonomy_linking_finds_related_taxonomies(self): - """ - Test: Linking works for all content types (products, taxonomies) - Task 20: Verify _find_taxonomy_candidates finds related taxonomies - """ - candidates = self.linker_service._find_taxonomy_candidates(self.taxonomy_content) - - # Should find related taxonomy - taxonomy_ids = [c['content_id'] for c in candidates] - self.assertIn(self.related_taxonomy.id, taxonomy_ids) - diff --git a/backend/igny8_core/business/optimization/tests/__init__.py b/backend/igny8_core/business/optimization/tests/__init__.py deleted file mode 100644 index 54386393..00000000 --- a/backend/igny8_core/business/optimization/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Optimization tests - diff --git a/backend/igny8_core/business/optimization/tests/test_analyzer.py b/backend/igny8_core/business/optimization/tests/test_analyzer.py deleted file mode 100644 index bbef02c8..00000000 --- a/backend/igny8_core/business/optimization/tests/test_analyzer.py +++ /dev/null @@ -1,177 +0,0 @@ -""" -Tests for ContentAnalyzer -""" -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.optimization.services.analyzer import ContentAnalyzer -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class ContentAnalyzerTests(IntegrationTestBase): - """Tests for ContentAnalyzer""" - - def setUp(self): - super().setUp() - self.analyzer = ContentAnalyzer() - - def test_analyze_returns_all_scores(self): - """Test that analyze returns all required scores""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="This is test content.
", - meta_title="Test Title", - meta_description="Test description", - primary_keyword="test keyword", - word_count=1500, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - self.assertIn('seo_score', scores) - self.assertIn('readability_score', scores) - self.assertIn('engagement_score', scores) - self.assertIn('overall_score', scores) - self.assertIn('word_count', scores) - self.assertIn('has_meta_title', scores) - self.assertIn('has_meta_description', scores) - self.assertIn('has_primary_keyword', scores) - self.assertIn('internal_links_count', scores) - - def test_analyze_returns_zero_scores_for_empty_content(self): - """Test that empty content returns zero scores""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Empty", - html_content="", - word_count=0, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - self.assertEqual(scores['seo_score'], 0) - self.assertEqual(scores['readability_score'], 0) - self.assertEqual(scores['engagement_score'], 0) - self.assertEqual(scores['overall_score'], 0) - - def test_calculate_seo_score_with_meta_title(self): - """Test SEO score calculation with meta title""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test", - meta_title="Test Title" * 5, # 50 chars - optimal length - word_count=1500, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - self.assertGreater(scores['seo_score'], 0) - - def test_calculate_seo_score_with_primary_keyword(self): - """Test SEO score calculation with primary keyword""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test", - primary_keyword="test keyword", - word_count=1500, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - self.assertGreater(scores['seo_score'], 0) - - def test_calculate_readability_score(self): - """Test readability score calculation""" - # Create content with good readability (short sentences, paragraphs) - html = "This is a sentence.
This is another sentence.
And one more.
" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test", - html_content=html, - word_count=20, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - self.assertGreater(scores['readability_score'], 0) - - def test_calculate_engagement_score_with_headings(self): - """Test engagement score calculation with headings""" - html = "Test content.
", - internal_links=[ - {'content_id': 1, 'anchor_text': 'link1'}, - {'content_id': 2, 'anchor_text': 'link2'}, - {'content_id': 3, 'anchor_text': 'link3'} - ], - word_count=100, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - self.assertGreater(scores['engagement_score'], 0) - self.assertEqual(scores['internal_links_count'], 3) - - def test_overall_score_is_weighted_average(self): - """Test that overall score is weighted average""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test", - html_content="Test content.
", - meta_title="Test Title", - meta_description="Test description", - primary_keyword="test", - word_count=1500, - status='draft' - ) - - scores = self.analyzer.analyze(content) - - # Overall should be weighted: SEO (40%) + Readability (30%) + Engagement (30%) - expected = ( - scores['seo_score'] * 0.4 + - scores['readability_score'] * 0.3 + - scores['engagement_score'] * 0.3 - ) - - self.assertAlmostEqual(scores['overall_score'], expected, places=1) - diff --git a/backend/igny8_core/business/optimization/tests/test_optimizer_service.py b/backend/igny8_core/business/optimization/tests/test_optimizer_service.py deleted file mode 100644 index 788d58bd..00000000 --- a/backend/igny8_core/business/optimization/tests/test_optimizer_service.py +++ /dev/null @@ -1,189 +0,0 @@ -""" -Tests for OptimizerService -""" -from unittest.mock import Mock, patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.optimization.models import OptimizationTask -from igny8_core.business.optimization.services.optimizer_service import OptimizerService -from igny8_core.business.billing.exceptions import InsufficientCreditsError -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class OptimizerServiceTests(IntegrationTestBase): - """Tests for OptimizerService""" - - def setUp(self): - super().setUp() - self.service = OptimizerService() - - # Create test content - self.content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="This is test content.
", - meta_title="Test Title", - meta_description="Test description", - primary_keyword="test keyword", - word_count=500, - status='draft', - source='igny8' - ) - - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits') - @patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze') - @patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation') - def test_optimize_from_writer(self, mock_deduct, mock_optimize, mock_analyze, mock_check): - """Test optimize_from_writer entry point""" - mock_check.return_value = True - mock_analyze.return_value = { - 'seo_score': 50.0, - 'readability_score': 60.0, - 'engagement_score': 55.0, - 'overall_score': 55.0 - } - - optimized_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Optimized Content", - html_content="Optimized content.
", - word_count=500, - status='draft', - source='igny8' - ) - mock_optimize.return_value = optimized_content - - result = self.service.optimize_from_writer(self.content.id) - - self.assertEqual(result.id, self.content.id) - mock_check.assert_called_once() - mock_deduct.assert_called_once() - - def test_optimize_from_writer_invalid_content(self): - """Test that ValueError is raised for invalid content""" - with self.assertRaises(ValueError): - self.service.optimize_from_writer(99999) - - def test_optimize_from_writer_wrong_source(self): - """Test that ValueError is raised for wrong source""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="WordPress Content", - word_count=100, - source='wordpress' - ) - - with self.assertRaises(ValueError): - self.service.optimize_from_writer(content.id) - - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits') - def test_optimize_insufficient_credits(self, mock_check): - """Test that InsufficientCreditsError is raised when credits are insufficient""" - mock_check.side_effect = InsufficientCreditsError("Insufficient credits") - - with self.assertRaises(InsufficientCreditsError): - self.service.optimize(self.content) - - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits') - @patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze') - @patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation') - def test_optimize_creates_optimization_task(self, mock_deduct, mock_optimize, mock_analyze, mock_check): - """Test that optimization creates OptimizationTask""" - mock_check.return_value = True - scores = { - 'seo_score': 50.0, - 'readability_score': 60.0, - 'engagement_score': 55.0, - 'overall_score': 55.0 - } - mock_analyze.return_value = scores - - optimized_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Optimized", - html_content="Optimized.
", - word_count=500, - status='draft' - ) - mock_optimize.return_value = optimized_content - - result = self.service.optimize(self.content) - - # Check that task was created - task = OptimizationTask.objects.filter(content=self.content).first() - self.assertIsNotNone(task) - self.assertEqual(task.status, 'completed') - self.assertEqual(task.scores_before, scores) - - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits') - @patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze') - def test_analyze_only_returns_scores(self, mock_analyze, mock_check): - """Test analyze_only method returns scores without optimizing""" - scores = { - 'seo_score': 50.0, - 'readability_score': 60.0, - 'engagement_score': 55.0, - 'overall_score': 55.0 - } - mock_analyze.return_value = scores - - result = self.service.analyze_only(self.content.id) - - self.assertEqual(result, scores) - mock_analyze.assert_called_once() - - def test_optimize_from_wordpress_sync(self): - """Test optimize_from_wordpress_sync entry point""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="WordPress Content", - word_count=100, - source='wordpress' - ) - - with patch.object(self.service, 'optimize') as mock_optimize: - mock_optimize.return_value = content - result = self.service.optimize_from_wordpress_sync(content.id) - - self.assertEqual(result.id, content.id) - mock_optimize.assert_called_once() - - def test_optimize_from_external_sync(self): - """Test optimize_from_external_sync entry point""" - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Shopify Content", - word_count=100, - source='shopify' - ) - - with patch.object(self.service, 'optimize') as mock_optimize: - mock_optimize.return_value = content - result = self.service.optimize_from_external_sync(content.id) - - self.assertEqual(result.id, content.id) - mock_optimize.assert_called_once() - - def test_optimize_manual(self): - """Test optimize_manual entry point""" - with patch.object(self.service, 'optimize') as mock_optimize: - mock_optimize.return_value = self.content - result = self.service.optimize_manual(self.content.id) - - self.assertEqual(result.id, self.content.id) - mock_optimize.assert_called_once() - diff --git a/backend/igny8_core/business/optimization/tests/test_universal_content_optimization.py b/backend/igny8_core/business/optimization/tests/test_universal_content_optimization.py deleted file mode 100644 index 40acaa19..00000000 --- a/backend/igny8_core/business/optimization/tests/test_universal_content_optimization.py +++ /dev/null @@ -1,184 +0,0 @@ -""" -Tests for Universal Content Types Optimization (Phase 8) -Tests for product and taxonomy optimization -""" -from unittest.mock import patch, MagicMock -from django.test import TestCase -from igny8_core.business.content.models import Content -from igny8_core.business.optimization.services.optimizer_service import OptimizerService -from igny8_core.business.optimization.models import OptimizationTask -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class UniversalContentOptimizationTests(IntegrationTestBase): - """Tests for Phase 8: Universal Content Types Optimization""" - - def setUp(self): - super().setUp() - # Add credits to account for testing - self.account.credits = 10000 - self.account.save() - self.optimizer_service = OptimizerService() - - # Create product content - self.product_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Product', - html_content='Product content that needs optimization.
', - entity_type='product', - json_blocks=[ - {'type': 'features', 'heading': 'Features', 'items': ['Feature 1']}, - {'type': 'specifications', 'heading': 'Specs', 'data': {'Spec': 'Value'}} - ], - structure_data={'product_type': 'software', 'price_range': '$99-$199'}, - word_count=1500, - status='draft' - ) - - # Create taxonomy content - self.taxonomy_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title='Test Taxonomy', - html_content='Taxonomy content that needs optimization.
', - entity_type='taxonomy', - json_blocks=[ - {'type': 'categories', 'heading': 'Categories', 'items': [{'name': 'Cat 1'}]}, - {'type': 'tags', 'heading': 'Tags', 'items': ['Tag 1']}, - {'type': 'hierarchy', 'heading': 'Hierarchy', 'structure': {}} - ], - word_count=1200, - status='draft' - ) - - @patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content') - @patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.get_credit_cost') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation') - def test_optimization_works_for_products(self, mock_deduct, mock_get_cost, mock_check_credits, mock_analyze, mock_optimize): - """ - Test: Optimization works for all content types (products, taxonomies) - Task 21: Verify product optimization includes product-specific metrics - """ - # Mock analyzer - mock_analyze.return_value = { - 'seo_score': 75, - 'readability_score': 80, - 'engagement_score': 70, - 'overall_score': 75 - } - - # Mock credit cost - mock_get_cost.return_value = 10 - - # Mock optimization - optimized_content = Content.objects.get(id=self.product_content.id) - optimized_content.html_content = 'Optimized product content.
' - mock_optimize.return_value = optimized_content - - # Optimize product - result = self.optimizer_service.optimize_product(self.product_content.id) - - # Verify result - self.assertIsNotNone(result) - self.assertEqual(result.entity_type, 'product') - self.assertEqual(result.optimizer_version, 1) - self.assertIsNotNone(result.optimization_scores) - - # Verify product-specific scores were enhanced - scores = result.optimization_scores - self.assertIn('product_completeness', scores) - self.assertGreaterEqual(scores['product_completeness'], 0) - self.assertLessEqual(scores['product_completeness'], 1) - - # Verify optimization task was created - task = OptimizationTask.objects.filter(content=result).first() - self.assertIsNotNone(task) - self.assertEqual(task.status, 'completed') - self.assertIn('product_completeness', task.scores_after) - - @patch('igny8_core.business.optimization.services.optimizer_service.OptimizerService._optimize_content') - @patch('igny8_core.business.optimization.services.optimizer_service.ContentAnalyzer.analyze') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.check_credits') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.get_credit_cost') - @patch('igny8_core.business.optimization.services.optimizer_service.CreditService.deduct_credits_for_operation') - def test_optimization_works_for_taxonomies(self, mock_deduct, mock_get_cost, mock_check_credits, mock_analyze, mock_optimize): - """ - Test: Optimization works for all content types (products, taxonomies) - Task 21: Verify taxonomy optimization includes taxonomy-specific metrics - """ - # Mock analyzer - mock_analyze.return_value = { - 'seo_score': 70, - 'readability_score': 75, - 'engagement_score': 65, - 'overall_score': 70 - } - - # Mock credit cost - mock_get_cost.return_value = 8 - - # Mock optimization - optimized_content = Content.objects.get(id=self.taxonomy_content.id) - optimized_content.html_content = 'Optimized taxonomy content.
' - mock_optimize.return_value = optimized_content - - # Optimize taxonomy - result = self.optimizer_service.optimize_taxonomy(self.taxonomy_content.id) - - # Verify result - self.assertIsNotNone(result) - self.assertEqual(result.entity_type, 'taxonomy') - self.assertEqual(result.optimizer_version, 1) - self.assertIsNotNone(result.optimization_scores) - - # Verify taxonomy-specific scores were enhanced - scores = result.optimization_scores - self.assertIn('taxonomy_organization', scores) - self.assertGreaterEqual(scores['taxonomy_organization'], 0) - self.assertLessEqual(scores['taxonomy_organization'], 1) - - # Verify optimization task was created - task = OptimizationTask.objects.filter(content=result).first() - self.assertIsNotNone(task) - self.assertEqual(task.status, 'completed') - self.assertIn('taxonomy_organization', task.scores_after) - - def test_enhance_product_scores_includes_completeness(self): - """ - Test: Optimization works for all content types (products, taxonomies) - Task 21: Verify _enhance_product_scores adds product_completeness - """ - base_scores = { - 'seo_score': 75, - 'readability_score': 80, - 'overall_score': 75 - } - - enhanced = self.optimizer_service._enhance_product_scores(base_scores, self.product_content) - - self.assertIn('product_completeness', enhanced) - self.assertGreaterEqual(enhanced['product_completeness'], 0) - self.assertLessEqual(enhanced['product_completeness'], 1) - - def test_enhance_taxonomy_scores_includes_organization(self): - """ - Test: Optimization works for all content types (products, taxonomies) - Task 21: Verify _enhance_taxonomy_scores adds taxonomy_organization - """ - base_scores = { - 'seo_score': 70, - 'readability_score': 75, - 'overall_score': 70 - } - - enhanced = self.optimizer_service._enhance_taxonomy_scores(base_scores, self.taxonomy_content) - - self.assertIn('taxonomy_organization', enhanced) - self.assertGreaterEqual(enhanced['taxonomy_organization'], 0) - self.assertLessEqual(enhanced['taxonomy_organization'], 1) - diff --git a/backend/igny8_core/business/publishing/tests/__init__.py b/backend/igny8_core/business/publishing/tests/__init__.py deleted file mode 100644 index c57a7935..00000000 --- a/backend/igny8_core/business/publishing/tests/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""" -Publishing Tests -Phase 5: Sites Renderer & Publishing -""" - diff --git a/backend/igny8_core/business/publishing/tests/test_adapters.py b/backend/igny8_core/business/publishing/tests/test_adapters.py deleted file mode 100644 index c86baba7..00000000 --- a/backend/igny8_core/business/publishing/tests/test_adapters.py +++ /dev/null @@ -1,139 +0,0 @@ -""" -Tests for Publishing Adapters -Phase 6: Site Integration & Multi-Destination Publishing -""" -from django.test import TestCase -from unittest.mock import Mock, patch - -from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector -from igny8_core.business.publishing.services.adapters.base_adapter import BaseAdapter -from igny8_core.business.publishing.services.adapters.sites_renderer_adapter import SitesRendererAdapter -from igny8_core.business.publishing.services.adapters.wordpress_adapter import WordPressAdapter -from igny8_core.business.site_building.models import SiteBlueprint - - -class AdapterPatternTestCase(TestCase): - """Test cases for adapter pattern""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - self.site = Site.objects.create( - account=self.account, - name="Test Site", - slug="test-site", - industry=self.industry - ) - self.sector = Sector.objects.create( - account=self.account, - site=self.site, - industry_sector=self.industry_sector, - name="Test Sector", - slug="test-sector" - ) - self.blueprint = SiteBlueprint.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - name="Test Blueprint", - status='ready' - ) - - def test_sites_renderer_adapter_implements_base_interface(self): - """Test: Adapter pattern works correctly""" - adapter = SitesRendererAdapter() - - self.assertIsInstance(adapter, BaseAdapter) - self.assertTrue(hasattr(adapter, 'publish')) - self.assertTrue(hasattr(adapter, 'test_connection')) - self.assertTrue(hasattr(adapter, 'get_status')) - - def test_wordpress_adapter_implements_base_interface(self): - """Test: Adapter pattern works correctly""" - adapter = WordPressAdapter() - - self.assertIsInstance(adapter, BaseAdapter) - self.assertTrue(hasattr(adapter, 'publish')) - self.assertTrue(hasattr(adapter, 'test_connection')) - self.assertTrue(hasattr(adapter, 'get_status')) - - def test_sites_renderer_adapter_deploys_site(self): - """Test: Multi-destination publishing works""" - adapter = SitesRendererAdapter() - - result = adapter.deploy(self.blueprint) - - self.assertTrue(result.get('success')) - self.assertIsNotNone(result.get('deployment_url')) - self.assertIsNotNone(result.get('version')) - - def test_wordpress_adapter_publishes_content(self): - """Test: Multi-destination publishing works""" - from igny8_core.business.content.models import Content - - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="Test
" - ) - - adapter = WordPressAdapter() - config = { - 'site_url': 'https://example.com', - 'username': 'test', - 'app_password': 'test' - } - - # Patch WordPressClient at the point where it's used in the adapter - with patch('igny8_core.business.publishing.services.adapters.wordpress_adapter.WordPressClient') as mock_client_class: - mock_instance = Mock() - mock_instance.create_post.return_value = {'id': 123, 'link': 'https://example.com/post/123'} - mock_client_class.return_value = mock_instance - - result = adapter.publish(content, config) - - self.assertTrue(result.get('success')) - self.assertIsNotNone(result.get('external_id')) - self.assertIsNotNone(result.get('url')) - diff --git a/backend/igny8_core/business/publishing/tests/test_deployment_service.py b/backend/igny8_core/business/publishing/tests/test_deployment_service.py deleted file mode 100644 index 29d81256..00000000 --- a/backend/igny8_core/business/publishing/tests/test_deployment_service.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -DEPRECATED: Tests for DeploymentService - SiteBlueprint models removed -Phase 5: Sites Renderer & Publishing -""" -from django.test import TestCase -from django.utils import timezone - -from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector -from igny8_core.business.publishing.models import DeploymentRecord -from igny8_core.business.publishing.services.deployment_service import DeploymentService - - -class DeploymentServiceTestCase(TestCase): - """DEPRECATED: Test cases for DeploymentService""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - self.site = Site.objects.create( - account=self.account, - name="Test Site", - slug="test-site", - industry=self.industry - ) - self.sector = Sector.objects.create( - account=self.account, - site=self.site, - industry_sector=self.industry_sector, - name="Test Sector", - slug="test-sector" - ) - # DEPRECATED: SiteBlueprint model removed - self.blueprint = None - self.service = DeploymentService() - - def test_get_status_returns_deployed_record(self): - """Test: Sites are accessible publicly""" - DeploymentRecord.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - site_blueprint=self.blueprint, - version=1, - status='deployed', - deployment_url='https://test-site.igny8.com', - deployed_at=timezone.now() - ) - - status = self.service.get_status(self.blueprint) - - self.assertIsNotNone(status) - self.assertEqual(status.status, 'deployed') - self.assertEqual(status.deployment_url, 'https://test-site.igny8.com') - - def test_get_latest_deployment_returns_most_recent(self): - """Test: Deployment works end-to-end""" - DeploymentRecord.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - site_blueprint=self.blueprint, - version=1, - status='failed', - created_at=timezone.now() - ) - - latest = DeploymentRecord.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - site_blueprint=self.blueprint, - version=2, - status='deployed', - deployment_url='https://test-site.igny8.com', - deployed_at=timezone.now() - ) - - result = self.service.get_latest_deployment(self.blueprint) - - self.assertIsNotNone(result) - self.assertEqual(result.version, 2) - self.assertEqual(result.status, 'deployed') - - def test_rollback_reverts_to_previous_version(self): - """Test: Deployment works end-to-end""" - DeploymentRecord.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - site_blueprint=self.blueprint, - version=1, - status='deployed', - deployment_url='https://test-site.igny8.com', - deployed_at=timezone.now() - ) - - result = self.service.rollback(self.blueprint, target_version=1) - - self.assertTrue(result.get('success')) - self.blueprint.refresh_from_db() - self.assertEqual(self.blueprint.deployed_version, 1) - diff --git a/backend/igny8_core/business/publishing/tests/test_publisher_service.py b/backend/igny8_core/business/publishing/tests/test_publisher_service.py deleted file mode 100644 index 18053d47..00000000 --- a/backend/igny8_core/business/publishing/tests/test_publisher_service.py +++ /dev/null @@ -1,151 +0,0 @@ -""" -Tests for PublisherService -Phase 5: Sites Renderer & Publishing -""" -from django.test import TestCase -from django.utils import timezone -from unittest.mock import Mock, patch - -from igny8_core.auth.models import Account, Site, Sector, User, Plan, Industry, IndustrySector -from igny8_core.business.site_building.models import SiteBlueprint -from igny8_core.business.publishing.models import PublishingRecord, DeploymentRecord -from igny8_core.business.publishing.services.publisher_service import PublisherService - - -class PublisherServiceTestCase(TestCase): - """Test cases for PublisherService""" - - def setUp(self): - """Set up test data""" - # Create plan first - self.plan = Plan.objects.create( - name="Test Plan", - slug="test-plan", - price=0, - credits_per_month=1000 - ) - - # Create user first (Account needs owner) - self.user = User.objects.create_user( - username='testuser', - email='test@test.com', - password='testpass123', - role='owner' - ) - - # Create account with owner - self.account = Account.objects.create( - name="Test Account", - slug="test-account", - plan=self.plan, - owner=self.user - ) - - # Update user to have account - self.user.account = self.account - self.user.save() - - # Create industry and sector - self.industry = Industry.objects.create( - name="Test Industry", - slug="test-industry" - ) - - self.industry_sector = IndustrySector.objects.create( - industry=self.industry, - name="Test Sector", - slug="test-sector" - ) - - self.site = Site.objects.create( - account=self.account, - name="Test Site", - slug="test-site", - industry=self.industry - ) - self.sector = Sector.objects.create( - account=self.account, - site=self.site, - industry_sector=self.industry_sector, - name="Test Sector", - slug="test-sector" - ) - self.blueprint = SiteBlueprint.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - name="Test Blueprint", - status='ready' - ) - self.service = PublisherService() - - def test_publish_to_sites_creates_deployment_record(self): - """Test: Deployment works end-to-end""" - # Don't mock deploy - let it run to create the deployment record - # But mock the filesystem operations to avoid actual file writes - with patch('igny8_core.business.publishing.services.adapters.sites_renderer_adapter.Path.mkdir'), \ - patch('igny8_core.business.publishing.services.adapters.sites_renderer_adapter.open', create=True) as mock_open: - mock_file = mock_open.return_value.__enter__.return_value - - result = self.service.publish_to_sites(self.blueprint) - - self.assertTrue(result.get('success')) - self.assertIsNotNone(result.get('deployment_url')) - - # Verify deployment record was created - deployment = DeploymentRecord.objects.filter(site_blueprint=self.blueprint).first() - self.assertIsNotNone(deployment) - - def test_get_deployment_status_returns_latest(self): - """Test: Sites are accessible publicly""" - DeploymentRecord.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - site_blueprint=self.blueprint, - version=1, - status='deployed', - deployment_url='https://test-site.igny8.com', - deployed_at=timezone.now() - ) - - status = self.service.get_deployment_status(self.blueprint) - - self.assertIsNotNone(status) - self.assertEqual(status.status, 'deployed') - self.assertIsNotNone(status.deployment_url) - - def test_publish_content_to_multiple_destinations(self): - """Test: Multi-destination publishing works""" - from igny8_core.business.content.models import Content - - content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="Test
" - ) - - with patch.object(self.service, '_get_adapter') as mock_get_adapter: - mock_adapter = Mock() - mock_adapter.publish.return_value = { - 'success': True, - 'external_id': '123', - 'url': 'https://example.com/post/123' - } - mock_get_adapter.return_value = mock_adapter - - result = self.service.publish_content( - content_id=content.id, - destinations=['wordpress', 'sites'], - account=self.account - ) - - self.assertTrue(result.get('success')) - self.assertEqual(len(result.get('results', [])), 2) - - # Verify publishing records were created - records = PublishingRecord.objects.filter(content=content) - self.assertEqual(records.count(), 2) - diff --git a/backend/igny8_core/modules/linker/tests/__init__.py b/backend/igny8_core/modules/linker/tests/__init__.py deleted file mode 100644 index 272a5cea..00000000 --- a/backend/igny8_core/modules/linker/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Linker module tests - diff --git a/backend/igny8_core/modules/linker/tests/test_views.py b/backend/igny8_core/modules/linker/tests/test_views.py deleted file mode 100644 index 123d2d7e..00000000 --- a/backend/igny8_core/modules/linker/tests/test_views.py +++ /dev/null @@ -1,137 +0,0 @@ -""" -Tests for Linker API endpoints -""" -from unittest.mock import patch -from django.test import TestCase -from rest_framework.test import APIClient -from rest_framework import status -from igny8_core.business.content.models import Content -from igny8_core.business.billing.exceptions import InsufficientCreditsError -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class LinkerAPITests(IntegrationTestBase): - """Tests for Linker API endpoints""" - - def setUp(self): - super().setUp() - self.client = APIClient() - self.client.force_authenticate(user=self.user) - - # Create test content - self.content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="Test content.
", - word_count=100, - status='draft' - ) - - def test_process_endpoint_requires_authentication(self): - """Test that process endpoint requires authentication""" - client = APIClient() # Not authenticated - response = client.post('/api/v1/linker/process/', { - 'content_id': self.content.id - }) - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - - @patch('igny8_core.modules.linker.views.LinkerService.process') - def test_process_endpoint_success(self, mock_process): - """Test successful processing""" - mock_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Linked Content", - html_content="Linked.
", - internal_links=[{'content_id': 1, 'anchor_text': 'test'}], - linker_version=1, - word_count=100 - ) - mock_process.return_value = mock_content - - response = self.client.post('/api/v1/linker/process/', { - 'content_id': self.content.id - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertEqual(response.data['data']['content_id'], self.content.id) - self.assertEqual(response.data['data']['links_added'], 1) - - def test_process_endpoint_invalid_content_id(self): - """Test process endpoint with invalid content ID""" - response = self.client.post('/api/v1/linker/process/', { - 'content_id': 99999 - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - @patch('igny8_core.modules.linker.views.LinkerService.process') - def test_process_endpoint_insufficient_credits(self, mock_process): - """Test process endpoint with insufficient credits""" - mock_process.side_effect = InsufficientCreditsError("Insufficient credits") - - response = self.client.post('/api/v1/linker/process/', { - 'content_id': self.content.id - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_402_PAYMENT_REQUIRED) - - @patch('igny8_core.modules.linker.views.LinkerService.batch_process') - def test_batch_process_endpoint_success(self, mock_batch): - """Test successful batch processing""" - content2 = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Content 2", - word_count=100 - ) - - mock_batch.return_value = [self.content, content2] - - response = self.client.post('/api/v1/linker/batch_process/', { - 'content_ids': [self.content.id, content2.id] - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertEqual(len(response.data['data']), 2) - - def test_batch_process_endpoint_validation(self): - """Test batch process endpoint validation""" - response = self.client.post('/api/v1/linker/batch_process/', { - 'content_ids': [] # Empty list - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_process_endpoint_respects_account_isolation(self): - """Test that process endpoint respects account isolation""" - from igny8_core.auth.models import Account - other_account = Account.objects.create( - name="Other Account", - slug="other", - plan=self.plan, - owner=self.user - ) - - other_content = Content.objects.create( - account=other_account, - site=self.site, - sector=self.sector, - title="Other Content", - word_count=100 - ) - - response = self.client.post('/api/v1/linker/process/', { - 'content_id': other_content.id - }, format='json') - - # Should return 400 because content belongs to different account - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - diff --git a/backend/igny8_core/modules/optimizer/tests/__init__.py b/backend/igny8_core/modules/optimizer/tests/__init__.py deleted file mode 100644 index 3d9b9227..00000000 --- a/backend/igny8_core/modules/optimizer/tests/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Optimizer module tests - diff --git a/backend/igny8_core/modules/optimizer/tests/test_views.py b/backend/igny8_core/modules/optimizer/tests/test_views.py deleted file mode 100644 index 1d0ae388..00000000 --- a/backend/igny8_core/modules/optimizer/tests/test_views.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -Tests for Optimizer API endpoints -""" -from unittest.mock import patch -from django.test import TestCase -from rest_framework.test import APIClient -from rest_framework import status -from igny8_core.business.content.models import Content -from igny8_core.business.optimization.models import OptimizationTask -from igny8_core.business.billing.exceptions import InsufficientCreditsError -from igny8_core.api.tests.test_integration_base import IntegrationTestBase - - -class OptimizerAPITests(IntegrationTestBase): - """Tests for Optimizer API endpoints""" - - def setUp(self): - super().setUp() - self.client = APIClient() - self.client.force_authenticate(user=self.user) - - # Create test content - self.content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Test Content", - html_content="Test content.
", - word_count=500, - status='draft', - source='igny8' - ) - - def test_optimize_endpoint_requires_authentication(self): - """Test that optimize endpoint requires authentication""" - client = APIClient() # Not authenticated - response = client.post('/api/v1/optimizer/optimize/', { - 'content_id': self.content.id - }) - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - - @patch('igny8_core.modules.optimizer.views.OptimizerService.optimize_from_writer') - def test_optimize_endpoint_success(self, mock_optimize): - """Test successful optimization""" - optimized_content = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Optimized", - html_content="Optimized.
", - word_count=500, - optimizer_version=1, - optimization_scores={'overall_score': 75.0} - ) - mock_optimize.return_value = optimized_content - - # Create optimization task - task = OptimizationTask.objects.create( - content=optimized_content, - scores_before={'overall_score': 50.0}, - scores_after={'overall_score': 75.0}, - status='completed', - account=self.account - ) - - response = self.client.post('/api/v1/optimizer/optimize/', { - 'content_id': self.content.id, - 'entry_point': 'writer' - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertEqual(response.data['data']['content_id'], self.content.id) - - def test_optimize_endpoint_all_entry_points(self): - """Test optimize endpoint with all entry point values""" - entry_points = ['auto', 'writer', 'wordpress', 'external', 'manual'] - - for entry_point in entry_points: - with patch(f'igny8_core.modules.optimizer.views.OptimizerService.optimize_{entry_point if entry_point != "auto" else "from_writer"}') as mock_opt: - if entry_point == 'auto': - mock_opt = patch('igny8_core.modules.optimizer.views.OptimizerService.optimize_from_writer') - mock_opt.return_value = self.content - - response = self.client.post('/api/v1/optimizer/optimize/', { - 'content_id': self.content.id, - 'entry_point': entry_point - }, format='json') - - # Should accept all entry points - self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]) - - @patch('igny8_core.modules.optimizer.views.OptimizerService.optimize_from_writer') - def test_batch_optimize_endpoint_success(self, mock_optimize): - """Test successful batch optimization""" - content2 = Content.objects.create( - account=self.account, - site=self.site, - sector=self.sector, - title="Content 2", - word_count=500, - source='igny8' - ) - - mock_optimize.return_value = self.content - - response = self.client.post('/api/v1/optimizer/batch_optimize/', { - 'content_ids': [self.content.id, content2.id], - 'entry_point': 'writer' - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertIn('succeeded', response.data['data']) - - @patch('igny8_core.modules.optimizer.views.OptimizerService.analyze_only') - def test_analyze_endpoint_success(self, mock_analyze): - """Test analyze endpoint returns scores""" - scores = { - 'seo_score': 50.0, - 'readability_score': 60.0, - 'engagement_score': 55.0, - 'overall_score': 55.0 - } - mock_analyze.return_value = scores - - response = self.client.post('/api/v1/optimizer/analyze/', { - 'content_id': self.content.id - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertTrue(response.data['success']) - self.assertIn('scores', response.data['data']) - self.assertEqual(response.data['data']['scores']['overall_score'], 55.0) - - @patch('igny8_core.modules.optimizer.views.OptimizerService.optimize_from_writer') - def test_optimize_endpoint_insufficient_credits(self, mock_optimize): - """Test optimize endpoint with insufficient credits""" - mock_optimize.side_effect = InsufficientCreditsError("Insufficient credits") - - response = self.client.post('/api/v1/optimizer/optimize/', { - 'content_id': self.content.id - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_402_PAYMENT_REQUIRED) - - def test_optimize_endpoint_invalid_content_id(self): - """Test optimize endpoint with invalid content ID""" - response = self.client.post('/api/v1/optimizer/optimize/', { - 'content_id': 99999 - }, format='json') - - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - - def test_optimize_endpoint_respects_account_isolation(self): - """Test that optimize endpoint respects account isolation""" - from igny8_core.auth.models import Account - other_account = Account.objects.create( - name="Other Account", - slug="other", - plan=self.plan, - owner=self.user - ) - - other_content = Content.objects.create( - account=other_account, - site=self.site, - sector=self.sector, - title="Other Content", - word_count=100 - ) - - response = self.client.post('/api/v1/optimizer/optimize/', { - 'content_id': other_content.id - }, format='json') - - # Should return 400 because content belongs to different account - self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - diff --git a/backend/igny8_core/test_settings.py b/backend/igny8_core/test_settings.py deleted file mode 100644 index 5abce93e..00000000 --- a/backend/igny8_core/test_settings.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Test settings - auto-clobber test database -""" -from igny8_core.settings import * - -# Auto-clobber test database -TEST_RUNNER = 'django.test.runner.DiscoverRunner' - diff --git a/backups/20260113_pre_v1_full_backup.sql b/backups/20260113_pre_v1_full_backup.sql new file mode 100644 index 00000000..7bc3ece2 --- /dev/null +++ b/backups/20260113_pre_v1_full_backup.sql @@ -0,0 +1,23348 @@ +-- +-- PostgreSQL database dump +-- + +\restrict PUVE48TDasY77PgrE54IWB6JYHS4RdcgJkolpMhpw3CQikDoinRpBJ8K3EZ8pTM + +-- Dumped from database version 15.14 (Debian 15.14-1.pgdg13+1) +-- Dumped by pg_dump version 15.14 (Debian 15.14-1.pgdg13+1) + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: admin_interface_theme; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.admin_interface_theme ( + id integer NOT NULL, + name character varying(50) NOT NULL, + active boolean NOT NULL, + title character varying(50) NOT NULL, + title_visible boolean NOT NULL, + logo character varying(100) NOT NULL, + logo_visible boolean NOT NULL, + css_header_background_color character varying(10) NOT NULL, + title_color character varying(10) NOT NULL, + css_header_text_color character varying(10) NOT NULL, + css_header_link_color character varying(10) NOT NULL, + css_header_link_hover_color character varying(10) NOT NULL, + css_module_background_color character varying(10) NOT NULL, + css_module_text_color character varying(10) NOT NULL, + css_module_link_color character varying(10) NOT NULL, + css_module_link_hover_color character varying(10) NOT NULL, + css_module_rounded_corners boolean NOT NULL, + css_generic_link_color character varying(10) NOT NULL, + css_generic_link_hover_color character varying(10) NOT NULL, + css_save_button_background_color character varying(10) NOT NULL, + css_save_button_background_hover_color character varying(10) NOT NULL, + css_save_button_text_color character varying(10) NOT NULL, + css_delete_button_background_color character varying(10) NOT NULL, + css_delete_button_background_hover_color character varying(10) NOT NULL, + css_delete_button_text_color character varying(10) NOT NULL, + list_filter_dropdown boolean NOT NULL, + related_modal_active boolean NOT NULL, + related_modal_background_color character varying(10) NOT NULL, + related_modal_rounded_corners boolean NOT NULL, + logo_color character varying(10) NOT NULL, + recent_actions_visible boolean NOT NULL, + favicon character varying(100) NOT NULL, + related_modal_background_opacity character varying(5) NOT NULL, + env_name character varying(50) NOT NULL, + env_visible_in_header boolean NOT NULL, + env_color character varying(10) NOT NULL, + env_visible_in_favicon boolean NOT NULL, + related_modal_close_button_visible boolean NOT NULL, + language_chooser_active boolean NOT NULL, + language_chooser_display character varying(10) NOT NULL, + list_filter_sticky boolean NOT NULL, + form_pagination_sticky boolean NOT NULL, + form_submit_sticky boolean NOT NULL, + css_module_background_selected_color character varying(10) NOT NULL, + css_module_link_selected_color character varying(10) NOT NULL, + css text NOT NULL +); + + +ALTER TABLE public.admin_interface_theme OWNER TO igny8; + +-- +-- Name: admin_interface_theme_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.admin_interface_theme ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.admin_interface_theme_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: auth_group; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.auth_group ( + id integer NOT NULL, + name character varying(150) NOT NULL +); + + +ALTER TABLE public.auth_group OWNER TO igny8; + +-- +-- Name: auth_group_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.auth_group ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.auth_group_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: auth_group_permissions; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.auth_group_permissions ( + id bigint NOT NULL, + group_id integer NOT NULL, + permission_id integer NOT NULL +); + + +ALTER TABLE public.auth_group_permissions OWNER TO igny8; + +-- +-- Name: auth_group_permissions_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.auth_group_permissions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.auth_group_permissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: auth_permission; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.auth_permission ( + id integer NOT NULL, + name character varying(255) NOT NULL, + content_type_id integer NOT NULL, + codename character varying(100) NOT NULL +); + + +ALTER TABLE public.auth_permission OWNER TO igny8; + +-- +-- Name: auth_permission_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.auth_permission ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.auth_permission_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: auth_user; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.auth_user ( + id integer NOT NULL, + password character varying(128) NOT NULL, + last_login timestamp with time zone, + is_superuser boolean NOT NULL, + username character varying(150) NOT NULL, + first_name character varying(150) NOT NULL, + last_name character varying(150) NOT NULL, + email character varying(254) NOT NULL, + is_staff boolean NOT NULL, + is_active boolean NOT NULL, + date_joined timestamp with time zone NOT NULL +); + + +ALTER TABLE public.auth_user OWNER TO igny8; + +-- +-- Name: auth_user_groups; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.auth_user_groups ( + id bigint NOT NULL, + user_id integer NOT NULL, + group_id integer NOT NULL +); + + +ALTER TABLE public.auth_user_groups OWNER TO igny8; + +-- +-- Name: auth_user_groups_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.auth_user_groups ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.auth_user_groups_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: auth_user_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.auth_user ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.auth_user_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: auth_user_user_permissions; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.auth_user_user_permissions ( + id bigint NOT NULL, + user_id integer NOT NULL, + permission_id integer NOT NULL +); + + +ALTER TABLE public.auth_user_user_permissions OWNER TO igny8; + +-- +-- Name: auth_user_user_permissions_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.auth_user_user_permissions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.auth_user_user_permissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: basefiles; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.basefiles ( + baseid integer NOT NULL, + jobid integer NOT NULL, + fileid bigint NOT NULL, + fileindex integer, + basejobid integer +); + + +ALTER TABLE public.basefiles OWNER TO igny8; + +-- +-- Name: basefiles_baseid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.basefiles_baseid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.basefiles_baseid_seq OWNER TO igny8; + +-- +-- Name: basefiles_baseid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.basefiles_baseid_seq OWNED BY public.basefiles.baseid; + + +-- +-- Name: billing_historicalaimodelconfig; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.billing_historicalaimodelconfig ( + id bigint NOT NULL, + model_name character varying(100) NOT NULL, + display_name character varying(200) NOT NULL, + model_type character varying(20) NOT NULL, + provider character varying(50) NOT NULL, + context_window integer, + is_active boolean NOT NULL, + is_default boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + history_id integer NOT NULL, + history_date timestamp with time zone NOT NULL, + history_change_reason character varying(100), + history_type character varying(1) NOT NULL, + history_user_id bigint, + credits_per_image integer, + quality_tier character varying(20), + tokens_per_credit integer, + capabilities jsonb NOT NULL, + cost_per_1k_input numeric(10,6), + cost_per_1k_output numeric(10,6), + max_tokens integer, + landscape_size character varying(20), + square_size character varying(20) NOT NULL, + valid_sizes jsonb NOT NULL +); + + +ALTER TABLE public.billing_historicalaimodelconfig OWNER TO igny8; + +-- +-- Name: billing_historicalaimodelconfig_history_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.billing_historicalaimodelconfig ALTER COLUMN history_id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.billing_historicalaimodelconfig_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: billing_historicalcreditcostconfig; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.billing_historicalcreditcostconfig ( + operation_type character varying(50) NOT NULL, + display_name character varying(100) NOT NULL, + description text NOT NULL, + is_active boolean NOT NULL, + history_id integer NOT NULL, + history_date timestamp with time zone NOT NULL, + history_change_reason character varying(100), + history_type character varying(1) NOT NULL, + history_user_id bigint, + calculation_mode character varying(20), + flat_rate_credits integer, + per_item_credits numeric(10,2), + per_item_unit integer, + base_credits integer NOT NULL +); + + +ALTER TABLE public.billing_historicalcreditcostconfig OWNER TO igny8; + +-- +-- Name: billing_historicalcreditcostconfig_history_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.billing_historicalcreditcostconfig ALTER COLUMN history_id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.billing_historicalcreditcostconfig_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: billing_historicalpayment; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.billing_historicalpayment ( + id bigint NOT NULL, + amount numeric(10,2) NOT NULL, + currency character varying(3) NOT NULL, + status character varying(20) NOT NULL, + payment_method character varying(50) NOT NULL, + stripe_payment_intent_id character varying(255), + stripe_charge_id character varying(255), + paypal_order_id character varying(255), + paypal_capture_id character varying(255), + manual_reference character varying(255), + manual_notes text NOT NULL, + admin_notes text NOT NULL, + approved_at timestamp with time zone, + processed_at timestamp with time zone, + failed_at timestamp with time zone, + refunded_at timestamp with time zone, + failure_reason text NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + history_id integer NOT NULL, + history_date timestamp with time zone NOT NULL, + history_change_reason character varying(100), + history_type character varying(1) NOT NULL, + tenant_id bigint, + approved_by_id bigint, + history_user_id bigint, + invoice_id bigint +); + + +ALTER TABLE public.billing_historicalpayment OWNER TO igny8; + +-- +-- Name: billing_historicalpayment_history_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.billing_historicalpayment ALTER COLUMN history_id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.billing_historicalpayment_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: cdimages; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.cdimages ( + mediaid integer NOT NULL, + lastburn timestamp without time zone NOT NULL +); + + +ALTER TABLE public.cdimages OWNER TO igny8; + +-- +-- Name: client; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.client ( + clientid integer NOT NULL, + name text NOT NULL, + uname text NOT NULL, + autoprune smallint DEFAULT 0, + fileretention bigint DEFAULT 0, + jobretention bigint DEFAULT 0 +); + + +ALTER TABLE public.client OWNER TO igny8; + +-- +-- Name: client_clientid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.client_clientid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.client_clientid_seq OWNER TO igny8; + +-- +-- Name: client_clientid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.client_clientid_seq OWNED BY public.client.clientid; + + +-- +-- Name: counters; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.counters ( + counter text NOT NULL, + minvalue integer DEFAULT 0, + maxvalue integer DEFAULT 0, + currentvalue integer DEFAULT 0, + wrapcounter text NOT NULL +); + + +ALTER TABLE public.counters OWNER TO igny8; + +-- +-- Name: device; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.device ( + deviceid integer NOT NULL, + name text NOT NULL, + mediatypeid integer NOT NULL, + storageid integer NOT NULL, + devmounts integer DEFAULT 0 NOT NULL, + devreadbytes bigint DEFAULT 0 NOT NULL, + devwritebytes bigint DEFAULT 0 NOT NULL, + devreadbytessincecleaning bigint DEFAULT 0 NOT NULL, + devwritebytessincecleaning bigint DEFAULT 0 NOT NULL, + devreadtime bigint DEFAULT 0 NOT NULL, + devwritetime bigint DEFAULT 0 NOT NULL, + devreadtimesincecleaning bigint DEFAULT 0 NOT NULL, + devwritetimesincecleaning bigint DEFAULT 0 NOT NULL, + cleaningdate timestamp without time zone, + cleaningperiod bigint DEFAULT 0 NOT NULL +); + + +ALTER TABLE public.device OWNER TO igny8; + +-- +-- Name: device_deviceid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.device_deviceid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.device_deviceid_seq OWNER TO igny8; + +-- +-- Name: device_deviceid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.device_deviceid_seq OWNED BY public.device.deviceid; + + +-- +-- Name: django_admin_log; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_admin_log ( + id integer NOT NULL, + action_time timestamp with time zone NOT NULL, + object_id text, + object_repr character varying(200) NOT NULL, + action_flag smallint NOT NULL, + change_message text NOT NULL, + content_type_id integer, + user_id bigint NOT NULL, + CONSTRAINT django_admin_log_action_flag_check CHECK ((action_flag >= 0)) +); + + +ALTER TABLE public.django_admin_log OWNER TO igny8; + +-- +-- Name: django_admin_log_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.django_admin_log ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.django_admin_log_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: django_celery_results_chordcounter; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_celery_results_chordcounter ( + id integer NOT NULL, + group_id character varying(255) NOT NULL, + sub_tasks text NOT NULL, + count integer NOT NULL, + CONSTRAINT django_celery_results_chordcounter_count_check CHECK ((count >= 0)) +); + + +ALTER TABLE public.django_celery_results_chordcounter OWNER TO igny8; + +-- +-- Name: django_celery_results_chordcounter_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.django_celery_results_chordcounter ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.django_celery_results_chordcounter_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: django_celery_results_groupresult; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_celery_results_groupresult ( + id integer NOT NULL, + group_id character varying(255) NOT NULL, + date_created timestamp with time zone NOT NULL, + date_done timestamp with time zone NOT NULL, + content_type character varying(128) NOT NULL, + content_encoding character varying(64) NOT NULL, + result text +); + + +ALTER TABLE public.django_celery_results_groupresult OWNER TO igny8; + +-- +-- Name: django_celery_results_groupresult_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.django_celery_results_groupresult ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.django_celery_results_groupresult_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: django_celery_results_taskresult; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_celery_results_taskresult ( + id integer NOT NULL, + task_id character varying(255) NOT NULL, + status character varying(50) NOT NULL, + content_type character varying(128) NOT NULL, + content_encoding character varying(64) NOT NULL, + result text, + date_done timestamp with time zone NOT NULL, + traceback text, + meta text, + task_args text, + task_kwargs text, + task_name character varying(255), + worker character varying(100), + date_created timestamp with time zone NOT NULL, + periodic_task_name character varying(255) +); + + +ALTER TABLE public.django_celery_results_taskresult OWNER TO igny8; + +-- +-- Name: django_celery_results_taskresult_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.django_celery_results_taskresult ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.django_celery_results_taskresult_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: django_content_type; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_content_type ( + id integer NOT NULL, + app_label character varying(100) NOT NULL, + model character varying(100) NOT NULL +); + + +ALTER TABLE public.django_content_type OWNER TO igny8; + +-- +-- Name: django_content_type_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.django_content_type ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.django_content_type_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: django_migrations; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_migrations ( + id bigint NOT NULL, + app character varying(255) NOT NULL, + name character varying(255) NOT NULL, + applied timestamp with time zone NOT NULL +); + + +ALTER TABLE public.django_migrations OWNER TO igny8; + +-- +-- Name: django_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.django_migrations ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.django_migrations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: django_session; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.django_session ( + session_key character varying(40) NOT NULL, + session_data text NOT NULL, + expire_date timestamp with time zone NOT NULL +); + + +ALTER TABLE public.django_session OWNER TO igny8; + +-- +-- Name: file; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.file ( + fileid bigint NOT NULL, + fileindex integer DEFAULT 0 NOT NULL, + jobid integer NOT NULL, + pathid integer NOT NULL, + filenameid integer NOT NULL, + deltaseq smallint DEFAULT 0 NOT NULL, + markid integer DEFAULT 0 NOT NULL, + lstat text NOT NULL, + md5 text NOT NULL +); + + +ALTER TABLE public.file OWNER TO igny8; + +-- +-- Name: file_fileid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.file_fileid_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.file_fileid_seq OWNER TO igny8; + +-- +-- Name: file_fileid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.file_fileid_seq OWNED BY public.file.fileid; + + +-- +-- Name: filename; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.filename ( + filenameid integer NOT NULL, + name text NOT NULL +); +ALTER TABLE ONLY public.filename ALTER COLUMN name SET STATISTICS 1000; + + +ALTER TABLE public.filename OWNER TO igny8; + +-- +-- Name: filename_filenameid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.filename_filenameid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.filename_filenameid_seq OWNER TO igny8; + +-- +-- Name: filename_filenameid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.filename_filenameid_seq OWNED BY public.filename.filenameid; + + +-- +-- Name: fileset; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.fileset ( + filesetid integer NOT NULL, + fileset text NOT NULL, + md5 text NOT NULL, + createtime timestamp without time zone NOT NULL +); + + +ALTER TABLE public.fileset OWNER TO igny8; + +-- +-- Name: fileset_filesetid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.fileset_filesetid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.fileset_filesetid_seq OWNER TO igny8; + +-- +-- Name: fileset_filesetid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.fileset_filesetid_seq OWNED BY public.fileset.filesetid; + + +-- +-- Name: igny8_account_payment_methods; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_account_payment_methods ( + id bigint NOT NULL, + type character varying(50) NOT NULL, + display_name character varying(100) NOT NULL, + is_default boolean NOT NULL, + is_enabled boolean NOT NULL, + is_verified boolean NOT NULL, + country_code character varying(2) NOT NULL, + instructions text NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_account_payment_methods OWNER TO igny8; + +-- +-- Name: igny8_account_payment_methods_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_account_payment_methods ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_account_payment_methods_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_account_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_account_settings ( + id bigint NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + key character varying(100) NOT NULL, + tenant_id bigint NOT NULL, + value jsonb NOT NULL +); + + +ALTER TABLE public.igny8_account_settings OWNER TO igny8; + +-- +-- Name: igny8_account_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_account_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_account_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_ai_model_config; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_ai_model_config ( + id bigint NOT NULL, + model_name character varying(100) NOT NULL, + display_name character varying(200) NOT NULL, + model_type character varying(20) NOT NULL, + provider character varying(50) NOT NULL, + context_window integer, + is_active boolean NOT NULL, + is_default boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + credits_per_image integer, + quality_tier character varying(20), + tokens_per_credit integer, + capabilities jsonb NOT NULL, + cost_per_1k_input numeric(10,6), + cost_per_1k_output numeric(10,6), + max_tokens integer, + landscape_size character varying(20), + square_size character varying(20) DEFAULT '1024x1024'::character varying, + valid_sizes jsonb DEFAULT '[]'::jsonb +); + + +ALTER TABLE public.igny8_ai_model_config OWNER TO igny8; + +-- +-- Name: igny8_ai_model_config_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_ai_model_config ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_ai_model_config_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_ai_prompts; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_ai_prompts ( + id bigint NOT NULL, + prompt_type character varying(50) NOT NULL, + prompt_value text NOT NULL, + is_active boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + is_customized boolean NOT NULL, + default_prompt text NOT NULL +); + + +ALTER TABLE public.igny8_ai_prompts OWNER TO igny8; + +-- +-- Name: igny8_ai_prompts_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_ai_prompts ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_ai_prompts_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_ai_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_ai_settings ( + id bigint NOT NULL, + integration_type character varying(50) NOT NULL, + config jsonb NOT NULL, + model_preferences jsonb NOT NULL, + cost_limits jsonb NOT NULL, + is_active boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_ai_settings OWNER TO igny8; + +-- +-- Name: igny8_ai_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_ai_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_ai_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_ai_task_logs; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_ai_task_logs ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + task_id character varying(255), + function_name character varying(100) NOT NULL, + phase character varying(50) NOT NULL, + message text NOT NULL, + status character varying(20) NOT NULL, + duration integer, + cost numeric(10,6) NOT NULL, + tokens integer NOT NULL, + request_steps jsonb NOT NULL, + response_steps jsonb NOT NULL, + error text, + payload jsonb, + result jsonb, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_ai_task_logs OWNER TO igny8; + +-- +-- Name: igny8_ai_task_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_ai_task_logs ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_ai_task_logs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_author_profiles; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_author_profiles ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text NOT NULL, + tone character varying(100) NOT NULL, + language character varying(50) NOT NULL, + structure_template jsonb NOT NULL, + is_active boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + is_custom boolean NOT NULL, + cloned_from_id bigint +); + + +ALTER TABLE public.igny8_author_profiles OWNER TO igny8; + +-- +-- Name: igny8_author_profiles_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_author_profiles ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_author_profiles_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_automation_configs; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_automation_configs ( + id bigint NOT NULL, + is_enabled boolean NOT NULL, + frequency character varying(20) NOT NULL, + scheduled_time time without time zone NOT NULL, + stage_1_batch_size integer NOT NULL, + stage_2_batch_size integer NOT NULL, + stage_3_batch_size integer NOT NULL, + stage_4_batch_size integer NOT NULL, + stage_5_batch_size integer NOT NULL, + stage_6_batch_size integer NOT NULL, + last_run_at timestamp with time zone, + next_run_at timestamp with time zone, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + account_id bigint NOT NULL, + site_id bigint NOT NULL, + within_stage_delay integer NOT NULL, + between_stage_delay integer NOT NULL +); + + +ALTER TABLE public.igny8_automation_configs OWNER TO igny8; + +-- +-- Name: igny8_automation_configs_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_automation_configs ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_automation_configs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_automation_rules; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_automation_rules ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text, + trigger character varying(50) NOT NULL, + schedule character varying(100), + conditions jsonb NOT NULL, + actions jsonb NOT NULL, + is_active boolean NOT NULL, + status character varying(50) NOT NULL, + last_executed_at timestamp with time zone, + execution_count integer NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_automation_rules OWNER TO igny8; + +-- +-- Name: igny8_automation_rules_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_automation_rules ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_automation_rules_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_automation_runs; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_automation_runs ( + id bigint NOT NULL, + run_id character varying(100) NOT NULL, + trigger_type character varying(20) NOT NULL, + status character varying(20) NOT NULL, + current_stage integer NOT NULL, + stage_1_result jsonb, + stage_2_result jsonb, + stage_3_result jsonb, + stage_4_result jsonb, + stage_5_result jsonb, + stage_6_result jsonb, + stage_7_result jsonb, + total_credits_used integer NOT NULL, + error_message text, + started_at timestamp with time zone NOT NULL, + completed_at timestamp with time zone, + account_id bigint NOT NULL, + site_id bigint NOT NULL, + cancelled_at timestamp with time zone, + paused_at timestamp with time zone, + resumed_at timestamp with time zone, + initial_snapshot jsonb NOT NULL +); + + +ALTER TABLE public.igny8_automation_runs OWNER TO igny8; + +-- +-- Name: igny8_automation_runs_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_automation_runs ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_automation_runs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_billing_configuration; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_billing_configuration ( + id bigint NOT NULL, + default_tokens_per_credit integer NOT NULL, + default_credit_price_usd numeric(10,4) NOT NULL, + enable_token_based_reporting boolean NOT NULL, + credit_rounding_mode character varying(10) NOT NULL, + updated_at timestamp with time zone NOT NULL, + updated_by_id bigint +); + + +ALTER TABLE public.igny8_billing_configuration OWNER TO igny8; + +-- +-- Name: igny8_billing_configuration_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_billing_configuration ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_billing_configuration_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_clusters; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_clusters ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text, + keywords_count integer NOT NULL, + volume integer NOT NULL, + mapped_pages integer NOT NULL, + status character varying(50) NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + disabled boolean NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_clusters OWNER TO igny8; + +-- +-- Name: igny8_clusters_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_clusters ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_clusters_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + content_html text NOT NULL, + word_count integer NOT NULL, + metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + title character varying(255), + meta_title character varying(255), + meta_description text, + primary_keyword character varying(255), + secondary_keywords jsonb DEFAULT '[]'::jsonb NOT NULL, + status character varying(50) NOT NULL, + generated_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp with time zone NOT NULL, + source character varying(50) NOT NULL, + sync_status character varying(50) DEFAULT ''::character varying, + external_id character varying(255), + external_url character varying(200), + sync_metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + internal_links jsonb DEFAULT '[]'::jsonb NOT NULL, + linker_version integer DEFAULT 0 NOT NULL, + optimizer_version integer DEFAULT 0 NOT NULL, + optimization_scores jsonb DEFAULT '{}'::jsonb NOT NULL, + content_type character varying(50) NOT NULL, + json_blocks jsonb DEFAULT '[]'::jsonb NOT NULL, + structure_data jsonb DEFAULT '{}'::jsonb NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + task_id bigint, + cluster_id bigint, + content_structure character varying(50), + content_format character varying(50), + external_type character varying(100) DEFAULT ''::character varying, + external_metadata jsonb DEFAULT '{}'::jsonb, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone, + site_status character varying(50) NOT NULL, + scheduled_publish_at timestamp with time zone, + site_status_updated_at timestamp with time zone +); + + +ALTER TABLE public.igny8_content OWNER TO igny8; + +-- +-- Name: igny8_content_attributes; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_attributes ( + id bigint NOT NULL, + attribute_type character varying(50) NOT NULL, + name character varying(120) NOT NULL, + value character varying(255), + external_id integer, + external_attribute_name character varying(100) NOT NULL, + source character varying(50) NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + cluster_id bigint, + content_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + task_id bigint +); + + +ALTER TABLE public.igny8_content_attributes OWNER TO igny8; + +-- +-- Name: igny8_content_attributes_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_attributes ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_attributes_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_cluster_map; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_cluster_map ( + id bigint NOT NULL, + role character varying(50) NOT NULL, + source character varying(50) NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + cluster_id bigint NOT NULL, + content_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + task_id bigint +); + + +ALTER TABLE public.igny8_content_cluster_map OWNER TO igny8; + +-- +-- Name: igny8_content_cluster_map_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_cluster_map ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_cluster_map_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_ideas; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_ideas ( + id bigint NOT NULL, + idea_title character varying(255) NOT NULL, + description text, + target_keywords character varying(500) NOT NULL, + status character varying(50) NOT NULL, + estimated_word_count integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + keyword_cluster_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + taxonomy_id bigint, + content_structure character varying(50) NOT NULL, + content_type character varying(50) NOT NULL, + disabled boolean NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_content_ideas OWNER TO igny8; + +-- +-- Name: igny8_content_ideas_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_ideas ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_ideas_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_ideas_keyword_objects; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_ideas_keyword_objects ( + id bigint NOT NULL, + contentideas_id bigint NOT NULL, + keywords_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_content_ideas_keyword_objects OWNER TO igny8; + +-- +-- Name: igny8_content_ideas_keyword_objects_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_ideas_keyword_objects ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_ideas_keyword_objects_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_taxonomy_map; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_taxonomy_map ( + id bigint NOT NULL, + source character varying(50) NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + content_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + taxonomy_id bigint NOT NULL, + task_id bigint +); + + +ALTER TABLE public.igny8_content_taxonomy_map OWNER TO igny8; + +-- +-- Name: igny8_content_taxonomy_map_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_taxonomy_map ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_taxonomy_map_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_taxonomy_relations; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_taxonomy_relations ( + id bigint NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + content_id bigint NOT NULL, + taxonomy_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_content_taxonomy_relations OWNER TO igny8; + +-- +-- Name: igny8_content_taxonomy_relations_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_taxonomy_relations ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_taxonomy_relations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_taxonomy_terms; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_taxonomy_terms ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + taxonomy_type character varying(50) NOT NULL, + description text NOT NULL, + external_id integer, + external_taxonomy character varying(100) NOT NULL, + sync_status character varying(50) NOT NULL, + count integer NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + parent_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_content_taxonomy_terms OWNER TO igny8; + +-- +-- Name: igny8_content_taxonomy_terms_clusters; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_content_taxonomy_terms_clusters ( + id bigint NOT NULL, + contenttaxonomy_id bigint NOT NULL, + clusters_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_content_taxonomy_terms_clusters OWNER TO igny8; + +-- +-- Name: igny8_content_taxonomy_terms_clusters_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_taxonomy_terms_clusters ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_taxonomy_terms_clusters_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_content_taxonomy_terms_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_content_taxonomy_terms ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_content_taxonomy_terms_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_core_auth_historicalaccount; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_core_auth_historicalaccount ( + id bigint NOT NULL, + is_deleted boolean NOT NULL, + deleted_at timestamp with time zone, + restore_until timestamp with time zone, + delete_reason character varying(255), + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + stripe_customer_id character varying(255), + credits integer NOT NULL, + status character varying(20) NOT NULL, + payment_method character varying(30) NOT NULL, + deletion_retention_days integer NOT NULL, + billing_email character varying(254), + billing_address_line1 character varying(255) NOT NULL, + billing_address_line2 character varying(255) NOT NULL, + billing_city character varying(100) NOT NULL, + billing_state character varying(100) NOT NULL, + billing_postal_code character varying(20) NOT NULL, + billing_country character varying(2) NOT NULL, + tax_id character varying(100) NOT NULL, + usage_period_start timestamp with time zone, + usage_period_end timestamp with time zone, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + history_id integer NOT NULL, + history_date timestamp with time zone NOT NULL, + history_change_reason character varying(100), + history_type character varying(1) NOT NULL, + deleted_by_id bigint, + history_user_id bigint, + owner_id bigint, + plan_id bigint, + usage_ahrefs_queries integer NOT NULL, + CONSTRAINT igny8_core_auth_historicalaccount_deletion_retention_days_check CHECK ((deletion_retention_days >= 0)) +); + + +ALTER TABLE public.igny8_core_auth_historicalaccount OWNER TO igny8; + +-- +-- Name: igny8_core_auth_historicalaccount_history_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_core_auth_historicalaccount ALTER COLUMN history_id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_core_auth_historicalaccount_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_credit_cost_config; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_credit_cost_config ( + operation_type character varying(50) NOT NULL, + display_name character varying(100) NOT NULL, + description text NOT NULL, + is_active boolean NOT NULL, + calculation_mode character varying(20) NOT NULL, + flat_rate_credits integer, + per_item_credits numeric(10,2), + per_item_unit integer, + base_credits integer NOT NULL +); + + +ALTER TABLE public.igny8_credit_cost_config OWNER TO igny8; + +-- +-- Name: igny8_credit_packages; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_credit_packages ( + id bigint NOT NULL, + name character varying(100) NOT NULL, + slug character varying(50) NOT NULL, + credits integer NOT NULL, + price numeric(10,2) NOT NULL, + discount_percentage integer NOT NULL, + stripe_product_id character varying(255), + stripe_price_id character varying(255), + paypal_plan_id character varying(255), + is_active boolean NOT NULL, + is_featured boolean NOT NULL, + description text NOT NULL, + features jsonb NOT NULL, + sort_order integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_credit_packages OWNER TO igny8; + +-- +-- Name: igny8_credit_packages_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_credit_packages ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_credit_packages_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_credit_transactions; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_credit_transactions ( + id bigint NOT NULL, + updated_at timestamp with time zone NOT NULL, + transaction_type character varying(20) NOT NULL, + amount integer NOT NULL, + balance_after integer NOT NULL, + description character varying(255) NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + reference_id character varying(255) NOT NULL, + payment_id bigint +); + + +ALTER TABLE public.igny8_credit_transactions OWNER TO igny8; + +-- +-- Name: igny8_credit_transactions_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_credit_transactions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_credit_transactions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_credit_usage_logs; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_credit_usage_logs ( + id bigint NOT NULL, + updated_at timestamp with time zone NOT NULL, + operation_type character varying(50) NOT NULL, + credits_used integer NOT NULL, + cost_usd numeric(10,4), + model_used character varying(100) NOT NULL, + tokens_input integer, + tokens_output integer, + related_object_type character varying(50) NOT NULL, + related_object_id integer, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + site_id bigint +); + + +ALTER TABLE public.igny8_credit_usage_logs OWNER TO igny8; + +-- +-- Name: igny8_credit_usage_logs_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_credit_usage_logs ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_credit_usage_logs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_deployment_records; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_deployment_records ( + id bigint NOT NULL, + version integer NOT NULL, + deployed_version integer, + status character varying(20) NOT NULL, + deployed_at timestamp with time zone, + deployment_url character varying(200), + error_message text, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + site_blueprint_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_deployment_records OWNER TO igny8; + +-- +-- Name: igny8_deployment_records_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_deployment_records ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_deployment_records_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_email_log; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_email_log ( + id bigint NOT NULL, + message_id character varying(200) NOT NULL, + to_email character varying(254) NOT NULL, + from_email character varying(254) NOT NULL, + subject character varying(500) NOT NULL, + template_name character varying(100) NOT NULL, + status character varying(20) NOT NULL, + provider character varying(50) NOT NULL, + error_message text NOT NULL, + tags jsonb NOT NULL, + sent_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_email_log OWNER TO igny8; + +-- +-- Name: igny8_email_log_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_email_log ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_email_log_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_email_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_email_settings ( + id bigint NOT NULL, + from_email character varying(254) NOT NULL, + from_name character varying(100) NOT NULL, + reply_to_email character varying(254) NOT NULL, + company_name character varying(100) NOT NULL, + company_address text NOT NULL, + logo_url character varying(200) NOT NULL, + support_email character varying(254) NOT NULL, + support_url character varying(200) NOT NULL, + unsubscribe_url character varying(200) NOT NULL, + send_welcome_emails boolean NOT NULL, + send_billing_emails boolean NOT NULL, + send_subscription_emails boolean NOT NULL, + send_low_credit_warnings boolean NOT NULL, + low_credit_threshold integer NOT NULL, + renewal_reminder_days integer NOT NULL, + updated_at timestamp with time zone NOT NULL, + updated_by_id bigint, + email_provider character varying(20) NOT NULL, + smtp_host character varying(255) NOT NULL, + smtp_password character varying(255) NOT NULL, + smtp_port integer NOT NULL, + smtp_timeout integer NOT NULL, + smtp_use_ssl boolean NOT NULL, + smtp_use_tls boolean NOT NULL, + smtp_username character varying(255) NOT NULL +); + + +ALTER TABLE public.igny8_email_settings OWNER TO igny8; + +-- +-- Name: igny8_email_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_email_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_email_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_email_templates; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_email_templates ( + id bigint NOT NULL, + template_name character varying(100) NOT NULL, + template_path character varying(200) NOT NULL, + display_name character varying(100) NOT NULL, + description text NOT NULL, + template_type character varying(20) NOT NULL, + default_subject character varying(200) NOT NULL, + required_context jsonb NOT NULL, + sample_context jsonb NOT NULL, + is_active boolean NOT NULL, + send_count integer NOT NULL, + last_sent_at timestamp with time zone, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_email_templates OWNER TO igny8; + +-- +-- Name: igny8_email_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_email_templates ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_email_templates_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_global_ai_prompts; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_global_ai_prompts ( + id bigint NOT NULL, + prompt_type character varying(50) NOT NULL, + prompt_value text NOT NULL, + description text NOT NULL, + variables jsonb NOT NULL, + is_active boolean NOT NULL, + version integer NOT NULL, + last_updated timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_global_ai_prompts OWNER TO igny8; + +-- +-- Name: igny8_global_ai_prompts_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_global_ai_prompts ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_global_ai_prompts_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_global_author_profiles; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_global_author_profiles ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text NOT NULL, + tone character varying(100) NOT NULL, + language character varying(50) NOT NULL, + structure_template jsonb NOT NULL, + category character varying(50) NOT NULL, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_global_author_profiles OWNER TO igny8; + +-- +-- Name: igny8_global_author_profiles_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_global_author_profiles ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_global_author_profiles_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_global_integration_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_global_integration_settings ( + id bigint NOT NULL, + openai_api_key character varying(500) NOT NULL, + openai_model character varying(100) NOT NULL, + openai_temperature double precision NOT NULL, + openai_max_tokens integer NOT NULL, + dalle_api_key character varying(500) NOT NULL, + dalle_model character varying(100) NOT NULL, + dalle_size character varying(20) NOT NULL, + runware_api_key character varying(500) NOT NULL, + is_active boolean NOT NULL, + last_updated timestamp with time zone NOT NULL, + updated_by_id bigint, + image_quality character varying(20) NOT NULL, + image_style character varying(30) NOT NULL, + runware_model character varying(100) NOT NULL, + desktop_image_size character varying(20) NOT NULL, + max_in_article_images integer NOT NULL, + default_image_service character varying(20) NOT NULL, + bria_api_key character varying(500) NOT NULL, + bria_model character varying(100) NOT NULL, + anthropic_api_key character varying(500) NOT NULL, + anthropic_model character varying(100) NOT NULL, + anthropic_temperature double precision NOT NULL, + anthropic_max_tokens integer NOT NULL, + default_text_provider character varying(20) NOT NULL +); + + +ALTER TABLE public.igny8_global_integration_settings OWNER TO igny8; + +-- +-- Name: igny8_global_integration_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_global_integration_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_global_integration_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_global_module_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_global_module_settings ( + id bigint NOT NULL, + planner_enabled boolean NOT NULL, + writer_enabled boolean NOT NULL, + thinker_enabled boolean NOT NULL, + automation_enabled boolean NOT NULL, + site_builder_enabled boolean NOT NULL, + linker_enabled boolean NOT NULL, + optimizer_enabled boolean NOT NULL, + publisher_enabled boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_global_module_settings OWNER TO igny8; + +-- +-- Name: igny8_global_module_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_global_module_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_global_module_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_global_strategies; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_global_strategies ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text NOT NULL, + prompt_types jsonb NOT NULL, + section_logic jsonb NOT NULL, + category character varying(50) NOT NULL, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_global_strategies OWNER TO igny8; + +-- +-- Name: igny8_global_strategies_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_global_strategies ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_global_strategies_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_images; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_images ( + id bigint NOT NULL, + image_type character varying(50) NOT NULL, + image_url character varying(500), + image_path character varying(500), + prompt text, + status character varying(50) NOT NULL, + "position" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + content_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + task_id bigint, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone, + caption text +); + + +ALTER TABLE public.igny8_images OWNER TO igny8; + +-- +-- Name: igny8_images_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_images ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_images_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_industries; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_industries ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + description text, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_industries OWNER TO igny8; + +-- +-- Name: igny8_industries_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_industries ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_industries_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_industry_sectors; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_industry_sectors ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + description text, + suggested_keywords jsonb NOT NULL, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + industry_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_industry_sectors OWNER TO igny8; + +-- +-- Name: igny8_industry_sectors_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_industry_sectors ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_industry_sectors_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_integration_providers; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_integration_providers ( + provider_id character varying(50) NOT NULL, + display_name character varying(100) NOT NULL, + provider_type character varying(20) NOT NULL, + api_key character varying(500) NOT NULL, + api_secret character varying(500) NOT NULL, + webhook_secret character varying(500) NOT NULL, + api_endpoint character varying(200) NOT NULL, + config jsonb NOT NULL, + is_active boolean NOT NULL, + is_sandbox boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + updated_by_id bigint +); + + +ALTER TABLE public.igny8_integration_providers OWNER TO igny8; + +-- +-- Name: igny8_integration_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_integration_settings ( + id bigint NOT NULL, + integration_type character varying(50) NOT NULL, + config jsonb NOT NULL, + is_active boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_integration_settings OWNER TO igny8; + +-- +-- Name: igny8_integration_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_integration_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_integration_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_invoices; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_invoices ( + id bigint NOT NULL, + invoice_number character varying(50) NOT NULL, + subtotal numeric(10,2) NOT NULL, + tax numeric(10,2) NOT NULL, + total numeric(10,2) NOT NULL, + status character varying(20) NOT NULL, + invoice_date date NOT NULL, + due_date date NOT NULL, + paid_at timestamp with time zone, + line_items jsonb NOT NULL, + stripe_invoice_id character varying(255), + payment_method character varying(50), + notes text NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + currency character varying(3) NOT NULL, + subscription_id bigint +); + + +ALTER TABLE public.igny8_invoices OWNER TO igny8; + +-- +-- Name: igny8_invoices_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_invoices ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_invoices_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_keywords; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_keywords ( + id bigint NOT NULL, + volume_override integer, + difficulty_override integer, + attribute_values jsonb NOT NULL, + status character varying(50) NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + cluster_id bigint, + sector_id bigint NOT NULL, + seed_keyword_id bigint NOT NULL, + site_id bigint NOT NULL, + disabled boolean NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_keywords OWNER TO igny8; + +-- +-- Name: igny8_keywords_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_keywords ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_keywords_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_module_enable_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_module_enable_settings ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + planner_enabled boolean NOT NULL, + writer_enabled boolean NOT NULL, + thinker_enabled boolean NOT NULL, + automation_enabled boolean NOT NULL, + site_builder_enabled boolean NOT NULL, + linker_enabled boolean NOT NULL, + optimizer_enabled boolean NOT NULL, + publisher_enabled boolean NOT NULL, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_module_enable_settings OWNER TO igny8; + +-- +-- Name: igny8_module_enable_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_module_enable_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_module_enable_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_module_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_module_settings ( + id bigint NOT NULL, + is_active boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + module_name character varying(100) NOT NULL, + key character varying(255) NOT NULL, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_module_settings OWNER TO igny8; + +-- +-- Name: igny8_module_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_module_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_module_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_optimization_tasks; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_optimization_tasks ( + id bigint NOT NULL, + scores_before jsonb NOT NULL, + scores_after jsonb NOT NULL, + html_before text NOT NULL, + html_after text NOT NULL, + status character varying(20) NOT NULL, + credits_used integer NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + content_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_optimization_tasks OWNER TO igny8; + +-- +-- Name: igny8_optimization_tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_optimization_tasks ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_optimization_tasks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_page_blueprints; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_page_blueprints ( + id bigint NOT NULL, + slug character varying(255) NOT NULL, + title character varying(255) NOT NULL, + type character varying(50) NOT NULL, + blocks_json jsonb NOT NULL, + status character varying(20) NOT NULL, + "order" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + site_blueprint_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_page_blueprints OWNER TO igny8; + +-- +-- Name: igny8_page_blueprints_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_page_blueprints ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_page_blueprints_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_password_reset_tokens; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_password_reset_tokens ( + id bigint NOT NULL, + token character varying(255) NOT NULL, + expires_at timestamp with time zone NOT NULL, + used boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + user_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_password_reset_tokens OWNER TO igny8; + +-- +-- Name: igny8_password_reset_tokens_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_password_reset_tokens ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_password_reset_tokens_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_payment_method_config; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_payment_method_config ( + id bigint NOT NULL, + country_code character varying(2) NOT NULL, + payment_method character varying(50) NOT NULL, + is_enabled boolean NOT NULL, + display_name character varying(100) NOT NULL, + instructions text NOT NULL, + bank_name character varying(255) NOT NULL, + account_number character varying(255) NOT NULL, + routing_number character varying(255) NOT NULL, + swift_code character varying(255) NOT NULL, + wallet_type character varying(100) NOT NULL, + wallet_id character varying(255) NOT NULL, + sort_order integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + account_title character varying(255) NOT NULL, + iban character varying(255) NOT NULL +); + + +ALTER TABLE public.igny8_payment_method_config OWNER TO igny8; + +-- +-- Name: igny8_payment_method_config_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_payment_method_config ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_payment_method_config_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_payments; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_payments ( + id bigint NOT NULL, + amount numeric(10,2) NOT NULL, + currency character varying(3) NOT NULL, + status character varying(20) NOT NULL, + payment_method character varying(50) NOT NULL, + stripe_payment_intent_id character varying(255), + stripe_charge_id character varying(255), + paypal_order_id character varying(255), + paypal_capture_id character varying(255), + manual_reference character varying(255), + manual_notes text NOT NULL, + approved_at timestamp with time zone, + processed_at timestamp with time zone, + failed_at timestamp with time zone, + refunded_at timestamp with time zone, + failure_reason text NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + approved_by_id bigint, + invoice_id bigint NOT NULL, + admin_notes text NOT NULL +); + + +ALTER TABLE public.igny8_payments OWNER TO igny8; + +-- +-- Name: igny8_payments_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_payments ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_payments_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_plan_limit_usage; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_plan_limit_usage ( + id bigint NOT NULL, + limit_type character varying(50) NOT NULL, + amount_used integer NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_plan_limit_usage OWNER TO igny8; + +-- +-- Name: igny8_plan_limit_usage_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_plan_limit_usage ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_plan_limit_usage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_plans; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_plans ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + price numeric(10,2) NOT NULL, + billing_cycle character varying(20) NOT NULL, + features jsonb NOT NULL, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + max_users integer NOT NULL, + max_sites integer NOT NULL, + max_industries integer, + max_author_profiles integer NOT NULL, + included_credits integer NOT NULL, + extra_credit_price numeric(10,2) NOT NULL, + allow_credit_topup boolean NOT NULL, + auto_credit_topup_threshold integer, + auto_credit_topup_amount integer, + stripe_product_id character varying(255), + stripe_price_id character varying(255), + credits_per_month integer NOT NULL, + is_internal boolean NOT NULL, + annual_discount_percent integer NOT NULL, + is_featured boolean NOT NULL, + max_keywords integer NOT NULL, + original_price numeric(10,2), + max_ahrefs_queries integer NOT NULL +); + + +ALTER TABLE public.igny8_plans OWNER TO igny8; + +-- +-- Name: igny8_plans_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_plans ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_plans_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_publishing_records; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_publishing_records ( + id bigint NOT NULL, + destination character varying(50) NOT NULL, + destination_id character varying(255), + destination_url character varying(200), + status character varying(20) NOT NULL, + published_at timestamp with time zone, + error_message text, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + content_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + site_blueprint_id bigint +); + + +ALTER TABLE public.igny8_publishing_records OWNER TO igny8; + +-- +-- Name: igny8_publishing_records_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_publishing_records ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_publishing_records_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_publishing_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_publishing_settings ( + id bigint NOT NULL, + auto_approval_enabled boolean NOT NULL, + auto_publish_enabled boolean NOT NULL, + daily_publish_limit integer NOT NULL, + weekly_publish_limit integer NOT NULL, + monthly_publish_limit integer NOT NULL, + publish_days jsonb NOT NULL, + publish_time_slots jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + site_id bigint NOT NULL, + CONSTRAINT igny8_publishing_settings_daily_publish_limit_check CHECK ((daily_publish_limit >= 0)), + CONSTRAINT igny8_publishing_settings_monthly_publish_limit_check CHECK ((monthly_publish_limit >= 0)), + CONSTRAINT igny8_publishing_settings_weekly_publish_limit_check CHECK ((weekly_publish_limit >= 0)) +); + + +ALTER TABLE public.igny8_publishing_settings OWNER TO igny8; + +-- +-- Name: igny8_publishing_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_publishing_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_publishing_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_scheduled_tasks; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_scheduled_tasks ( + id bigint NOT NULL, + scheduled_at timestamp with time zone NOT NULL, + executed_at timestamp with time zone, + status character varying(50) NOT NULL, + result jsonb NOT NULL, + error_message text, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + automation_rule_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_scheduled_tasks OWNER TO igny8; + +-- +-- Name: igny8_scheduled_tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_scheduled_tasks ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_scheduled_tasks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_sectors; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_sectors ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + description text, + is_active boolean NOT NULL, + status character varying(20) NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + industry_sector_id bigint, + site_id bigint NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_sectors OWNER TO igny8; + +-- +-- Name: igny8_sectors_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_sectors ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_sectors_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_seed_keywords; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_seed_keywords ( + id bigint NOT NULL, + keyword character varying(255) NOT NULL, + volume integer NOT NULL, + difficulty integer NOT NULL, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + industry_id bigint NOT NULL, + sector_id bigint NOT NULL, + country character varying(2) NOT NULL +); + + +ALTER TABLE public.igny8_seed_keywords OWNER TO igny8; + +-- +-- Name: igny8_seed_keywords_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_seed_keywords ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_seed_keywords_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_blueprint_clusters; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_blueprint_clusters ( + id bigint NOT NULL, + role character varying(50) NOT NULL, + coverage_status character varying(50) NOT NULL, + metadata jsonb NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + cluster_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + site_blueprint_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_site_blueprint_clusters OWNER TO igny8; + +-- +-- Name: igny8_site_blueprint_clusters_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_blueprint_clusters ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_blueprint_clusters_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_blueprint_taxonomies; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_blueprint_taxonomies ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + taxonomy_type character varying(50) NOT NULL, + description text, + metadata jsonb NOT NULL, + external_reference character varying(255), + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + site_blueprint_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_site_blueprint_taxonomies OWNER TO igny8; + +-- +-- Name: igny8_site_blueprint_taxonomies_clusters; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_blueprint_taxonomies_clusters ( + id bigint NOT NULL, + siteblueprinttaxonomy_id bigint NOT NULL, + clusters_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_site_blueprint_taxonomies_clusters OWNER TO igny8; + +-- +-- Name: igny8_site_blueprint_taxonomies_clusters_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_blueprint_taxonomies_clusters ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_blueprint_taxonomies_clusters_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_blueprint_taxonomies_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_blueprint_taxonomies ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_blueprint_taxonomies_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_blueprints; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_blueprints ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text, + config_json jsonb NOT NULL, + structure_json jsonb NOT NULL, + status character varying(20) NOT NULL, + hosting_type character varying(50) NOT NULL, + version integer NOT NULL, + deployed_version integer, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint NOT NULL, + site_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_site_blueprints OWNER TO igny8; + +-- +-- Name: igny8_site_blueprints_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_blueprints ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_blueprints_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_builder_audience_profiles; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_builder_audience_profiles ( + id bigint NOT NULL, + name character varying(120) NOT NULL, + description character varying(255) NOT NULL, + is_active boolean NOT NULL, + "order" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + CONSTRAINT igny8_site_builder_audience_profiles_order_check CHECK (("order" >= 0)) +); + + +ALTER TABLE public.igny8_site_builder_audience_profiles OWNER TO igny8; + +-- +-- Name: igny8_site_builder_audience_profiles_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_builder_audience_profiles ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_builder_audience_profiles_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_builder_brand_personalities; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_builder_brand_personalities ( + id bigint NOT NULL, + name character varying(120) NOT NULL, + description character varying(255) NOT NULL, + is_active boolean NOT NULL, + "order" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + CONSTRAINT igny8_site_builder_brand_personalities_order_check CHECK (("order" >= 0)) +); + + +ALTER TABLE public.igny8_site_builder_brand_personalities OWNER TO igny8; + +-- +-- Name: igny8_site_builder_brand_personalities_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_builder_brand_personalities ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_builder_brand_personalities_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_builder_business_types; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_builder_business_types ( + id bigint NOT NULL, + name character varying(120) NOT NULL, + description character varying(255) NOT NULL, + is_active boolean NOT NULL, + "order" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + CONSTRAINT igny8_site_builder_business_types_order_check CHECK (("order" >= 0)) +); + + +ALTER TABLE public.igny8_site_builder_business_types OWNER TO igny8; + +-- +-- Name: igny8_site_builder_business_types_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_builder_business_types ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_builder_business_types_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_builder_hero_imagery; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_builder_hero_imagery ( + id bigint NOT NULL, + name character varying(120) NOT NULL, + description character varying(255) NOT NULL, + is_active boolean NOT NULL, + "order" integer NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + CONSTRAINT igny8_site_builder_hero_imagery_order_check CHECK (("order" >= 0)) +); + + +ALTER TABLE public.igny8_site_builder_hero_imagery OWNER TO igny8; + +-- +-- Name: igny8_site_builder_hero_imagery_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_builder_hero_imagery ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_builder_hero_imagery_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_integrations; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_integrations ( + id bigint NOT NULL, + platform character varying(50) NOT NULL, + platform_type character varying(50) NOT NULL, + config_json jsonb NOT NULL, + credentials_json jsonb NOT NULL, + is_active boolean NOT NULL, + sync_enabled boolean NOT NULL, + last_sync_at timestamp with time zone, + sync_status character varying(20) NOT NULL, + sync_error text, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + site_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_site_integrations OWNER TO igny8; + +-- +-- Name: igny8_site_integrations_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_integrations ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_integrations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_site_user_access; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_site_user_access ( + id bigint NOT NULL, + granted_at timestamp with time zone NOT NULL, + granted_by_id bigint, + site_id bigint NOT NULL, + user_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_site_user_access OWNER TO igny8; + +-- +-- Name: igny8_site_user_access_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_site_user_access ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_site_user_access_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_sites; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_sites ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + domain character varying(200), + description text, + is_active boolean NOT NULL, + status character varying(20) NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + wp_url character varying(200), + wp_username character varying(255), + wp_app_password character varying(255), + site_type character varying(50) NOT NULL, + hosting_type character varying(50) NOT NULL, + seo_metadata jsonb NOT NULL, + tenant_id bigint NOT NULL, + industry_id bigint NOT NULL, + wp_api_key character varying(255), + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_sites OWNER TO igny8; + +-- +-- Name: igny8_sites_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_sites ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_sites_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_strategies; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_strategies ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + description text NOT NULL, + prompt_types jsonb NOT NULL, + section_logic jsonb NOT NULL, + is_active boolean NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + sector_id bigint, + is_custom boolean NOT NULL, + cloned_from_id bigint +); + + +ALTER TABLE public.igny8_strategies OWNER TO igny8; + +-- +-- Name: igny8_strategies_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_strategies ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_strategies_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_subscriptions; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_subscriptions ( + id bigint NOT NULL, + stripe_subscription_id character varying(255), + status character varying(20) NOT NULL, + current_period_start timestamp with time zone NOT NULL, + current_period_end timestamp with time zone NOT NULL, + cancel_at_period_end boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + external_payment_id character varying(255), + plan_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_subscriptions OWNER TO igny8; + +-- +-- Name: igny8_subscriptions_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_subscriptions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_subscriptions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_sync_events; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_sync_events ( + id bigint NOT NULL, + updated_at timestamp with time zone NOT NULL, + event_type character varying(50) NOT NULL, + action character varying(100) NOT NULL, + description text NOT NULL, + success boolean NOT NULL, + content_id integer, + external_id character varying(255), + details jsonb NOT NULL, + error_message text, + duration_ms integer, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + integration_id bigint NOT NULL, + site_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_sync_events OWNER TO igny8; + +-- +-- Name: igny8_sync_events_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_sync_events ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_sync_events_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_system_ai_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_system_ai_settings ( + id bigint NOT NULL, + temperature double precision DEFAULT 0.7 NOT NULL, + max_tokens integer DEFAULT 8192 NOT NULL, + image_style character varying(30) DEFAULT 'photorealistic'::character varying NOT NULL, + image_quality character varying(20) DEFAULT 'standard'::character varying NOT NULL, + max_images_per_article integer DEFAULT 4 NOT NULL, + image_size character varying(20) DEFAULT '1024x1024'::character varying NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + updated_by_id integer, + default_quality_tier character varying(20) NOT NULL, + max_allowed_images integer NOT NULL +); + + +ALTER TABLE public.igny8_system_ai_settings OWNER TO igny8; + +-- +-- Name: igny8_system_ai_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.igny8_system_ai_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.igny8_system_ai_settings_id_seq OWNER TO igny8; + +-- +-- Name: igny8_system_ai_settings_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.igny8_system_ai_settings_id_seq OWNED BY public.igny8_system_ai_settings.id; + + +-- +-- Name: igny8_system_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_system_settings ( + id bigint NOT NULL, + key character varying(255) NOT NULL, + value jsonb NOT NULL, + description text NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_system_settings OWNER TO igny8; + +-- +-- Name: igny8_system_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_system_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_system_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_tasks; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_tasks ( + id bigint NOT NULL, + title character varying(255) NOT NULL, + description text, + keywords character varying(500) NOT NULL, + status character varying(50) NOT NULL, + content_type character varying(50), + content_structure character varying(50), + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + cluster_id bigint, + idea_id bigint, + sector_id bigint NOT NULL, + site_id bigint NOT NULL, + taxonomy_id bigint, + word_count integer NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone +); + + +ALTER TABLE public.igny8_tasks OWNER TO igny8; + +-- +-- Name: igny8_tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_tasks ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_tasks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_tasks_keyword_objects; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_tasks_keyword_objects ( + id bigint NOT NULL, + tasks_id bigint NOT NULL, + keywords_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_tasks_keyword_objects OWNER TO igny8; + +-- +-- Name: igny8_tasks_keyword_objects_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_tasks_keyword_objects ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_tasks_keyword_objects_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_tenants; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_tenants ( + id bigint NOT NULL, + name character varying(255) NOT NULL, + slug character varying(255) NOT NULL, + stripe_customer_id character varying(255), + credits integer NOT NULL, + status character varying(20) NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + owner_id bigint, + plan_id bigint NOT NULL, + billing_address_line1 character varying(255) NOT NULL, + billing_address_line2 character varying(255) NOT NULL, + billing_city character varying(100) NOT NULL, + billing_country character varying(2) NOT NULL, + billing_email character varying(254), + billing_postal_code character varying(20) NOT NULL, + billing_state character varying(100) NOT NULL, + tax_id character varying(100) NOT NULL, + delete_reason character varying(255), + deleted_at timestamp with time zone, + deleted_by_id bigint, + deletion_retention_days integer NOT NULL, + is_deleted boolean NOT NULL, + restore_until timestamp with time zone, + payment_method character varying(30) NOT NULL, + usage_period_end timestamp with time zone, + usage_period_start timestamp with time zone, + usage_ahrefs_queries integer NOT NULL, + CONSTRAINT igny8_tenants_deletion_retention_days_check CHECK ((deletion_retention_days >= 0)) +); + + +ALTER TABLE public.igny8_tenants OWNER TO igny8; + +-- +-- Name: igny8_tenants_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_tenants ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_tenants_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_user_settings; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_user_settings ( + id bigint NOT NULL, + key character varying(255) NOT NULL, + value jsonb NOT NULL, + updated_at timestamp with time zone NOT NULL, + created_at timestamp with time zone NOT NULL, + tenant_id bigint NOT NULL, + user_id bigint NOT NULL +); + + +ALTER TABLE public.igny8_user_settings OWNER TO igny8; + +-- +-- Name: igny8_user_settings_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_user_settings ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_user_settings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_users; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_users ( + id bigint NOT NULL, + password character varying(128) NOT NULL, + last_login timestamp with time zone, + is_superuser boolean NOT NULL, + username character varying(150) NOT NULL, + first_name character varying(150) NOT NULL, + last_name character varying(150) NOT NULL, + is_staff boolean NOT NULL, + is_active boolean NOT NULL, + date_joined timestamp with time zone NOT NULL, + role character varying(20) NOT NULL, + email character varying(254) NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + tenant_id bigint +); + + +ALTER TABLE public.igny8_users OWNER TO igny8; + +-- +-- Name: igny8_users_groups; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_users_groups ( + id bigint NOT NULL, + user_id bigint NOT NULL, + group_id integer NOT NULL +); + + +ALTER TABLE public.igny8_users_groups OWNER TO igny8; + +-- +-- Name: igny8_users_groups_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_users_groups ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_users_groups_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_users_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_users ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_users_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_users_user_permissions; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_users_user_permissions ( + id bigint NOT NULL, + user_id bigint NOT NULL, + permission_id integer NOT NULL +); + + +ALTER TABLE public.igny8_users_user_permissions OWNER TO igny8; + +-- +-- Name: igny8_users_user_permissions_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_users_user_permissions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_users_user_permissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: igny8_webhook_events; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.igny8_webhook_events ( + id bigint NOT NULL, + event_id character varying(255) NOT NULL, + provider character varying(20) NOT NULL, + event_type character varying(100) NOT NULL, + payload jsonb NOT NULL, + processed boolean NOT NULL, + processed_at timestamp with time zone, + error_message text NOT NULL, + retry_count integer NOT NULL, + created_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.igny8_webhook_events OWNER TO igny8; + +-- +-- Name: igny8_webhook_events_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.igny8_webhook_events ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.igny8_webhook_events_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: job; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.job ( + jobid integer NOT NULL, + job text NOT NULL, + name text NOT NULL, + type character(1) NOT NULL, + level character(1) NOT NULL, + clientid integer DEFAULT 0, + jobstatus character(1) NOT NULL, + schedtime timestamp without time zone, + starttime timestamp without time zone, + endtime timestamp without time zone, + realendtime timestamp without time zone, + jobtdate bigint DEFAULT 0, + volsessionid integer DEFAULT 0, + volsessiontime integer DEFAULT 0, + jobfiles integer DEFAULT 0, + jobbytes bigint DEFAULT 0, + readbytes bigint DEFAULT 0, + joberrors integer DEFAULT 0, + jobmissingfiles integer DEFAULT 0, + poolid integer DEFAULT 0, + filesetid integer DEFAULT 0, + priorjobid integer DEFAULT 0, + purgedfiles smallint DEFAULT 0, + hasbase smallint DEFAULT 0, + hascache smallint DEFAULT 0, + reviewed smallint DEFAULT 0, + comment text, + filetable text DEFAULT 'File'::text +); + + +ALTER TABLE public.job OWNER TO igny8; + +-- +-- Name: job_jobid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.job_jobid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.job_jobid_seq OWNER TO igny8; + +-- +-- Name: job_jobid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.job_jobid_seq OWNED BY public.job.jobid; + + +-- +-- Name: jobhisto; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.jobhisto ( + jobid integer NOT NULL, + job text NOT NULL, + name text NOT NULL, + type character(1) NOT NULL, + level character(1) NOT NULL, + clientid integer, + jobstatus character(1) NOT NULL, + schedtime timestamp without time zone, + starttime timestamp without time zone, + endtime timestamp without time zone, + realendtime timestamp without time zone, + jobtdate bigint, + volsessionid integer, + volsessiontime integer, + jobfiles integer, + jobbytes bigint, + readbytes bigint, + joberrors integer, + jobmissingfiles integer, + poolid integer, + filesetid integer, + priorjobid integer, + purgedfiles smallint, + hasbase smallint, + hascache smallint, + reviewed smallint, + comment text, + filetable text +); + + +ALTER TABLE public.jobhisto OWNER TO igny8; + +-- +-- Name: jobmedia; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.jobmedia ( + jobmediaid integer NOT NULL, + jobid integer NOT NULL, + mediaid integer NOT NULL, + firstindex integer DEFAULT 0, + lastindex integer DEFAULT 0, + startfile integer DEFAULT 0, + endfile integer DEFAULT 0, + startblock bigint DEFAULT 0, + endblock bigint DEFAULT 0, + volindex integer DEFAULT 0 +); + + +ALTER TABLE public.jobmedia OWNER TO igny8; + +-- +-- Name: jobmedia_jobmediaid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.jobmedia_jobmediaid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.jobmedia_jobmediaid_seq OWNER TO igny8; + +-- +-- Name: jobmedia_jobmediaid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.jobmedia_jobmediaid_seq OWNED BY public.jobmedia.jobmediaid; + + +-- +-- Name: location; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.location ( + locationid integer NOT NULL, + location text NOT NULL, + cost integer DEFAULT 0, + enabled smallint +); + + +ALTER TABLE public.location OWNER TO igny8; + +-- +-- Name: location_locationid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.location_locationid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.location_locationid_seq OWNER TO igny8; + +-- +-- Name: location_locationid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.location_locationid_seq OWNED BY public.location.locationid; + + +-- +-- Name: locationlog; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.locationlog ( + loclogid integer NOT NULL, + date timestamp without time zone, + comment text NOT NULL, + mediaid integer DEFAULT 0, + locationid integer DEFAULT 0, + newvolstatus text NOT NULL, + newenabled smallint, + CONSTRAINT locationlog_newvolstatus_check CHECK ((newvolstatus = ANY (ARRAY['Full'::text, 'Archive'::text, 'Append'::text, 'Recycle'::text, 'Purged'::text, 'Read-Only'::text, 'Disabled'::text, 'Error'::text, 'Busy'::text, 'Used'::text, 'Cleaning'::text, 'Scratch'::text]))) +); + + +ALTER TABLE public.locationlog OWNER TO igny8; + +-- +-- Name: locationlog_loclogid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.locationlog_loclogid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.locationlog_loclogid_seq OWNER TO igny8; + +-- +-- Name: locationlog_loclogid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.locationlog_loclogid_seq OWNED BY public.locationlog.loclogid; + + +-- +-- Name: log; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.log ( + logid integer NOT NULL, + jobid integer NOT NULL, + "time" timestamp without time zone, + logtext text NOT NULL +); + + +ALTER TABLE public.log OWNER TO igny8; + +-- +-- Name: log_logid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.log_logid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.log_logid_seq OWNER TO igny8; + +-- +-- Name: log_logid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.log_logid_seq OWNED BY public.log.logid; + + +-- +-- Name: media; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.media ( + mediaid integer NOT NULL, + volumename text NOT NULL, + slot integer DEFAULT 0, + poolid integer DEFAULT 0, + mediatype text NOT NULL, + mediatypeid integer DEFAULT 0, + labeltype integer DEFAULT 0, + firstwritten timestamp without time zone, + lastwritten timestamp without time zone, + labeldate timestamp without time zone, + voljobs integer DEFAULT 0, + volfiles integer DEFAULT 0, + volblocks integer DEFAULT 0, + volmounts integer DEFAULT 0, + volbytes bigint DEFAULT 0, + volabytes bigint DEFAULT 0, + volapadding bigint DEFAULT 0, + volholebytes bigint DEFAULT 0, + volholes integer DEFAULT 0, + volparts integer DEFAULT 0, + volerrors integer DEFAULT 0, + volwrites bigint DEFAULT 0, + volcapacitybytes bigint DEFAULT 0, + volstatus text NOT NULL, + enabled smallint DEFAULT 1, + recycle smallint DEFAULT 0, + actiononpurge smallint DEFAULT 0, + volretention bigint DEFAULT 0, + voluseduration bigint DEFAULT 0, + maxvoljobs integer DEFAULT 0, + maxvolfiles integer DEFAULT 0, + maxvolbytes bigint DEFAULT 0, + inchanger smallint DEFAULT 0, + storageid integer DEFAULT 0, + deviceid integer DEFAULT 0, + mediaaddressing smallint DEFAULT 0, + volreadtime bigint DEFAULT 0, + volwritetime bigint DEFAULT 0, + endfile integer DEFAULT 0, + endblock bigint DEFAULT 0, + locationid integer DEFAULT 0, + recyclecount integer DEFAULT 0, + initialwrite timestamp without time zone, + scratchpoolid integer DEFAULT 0, + recyclepoolid integer DEFAULT 0, + comment text, + CONSTRAINT media_volstatus_check CHECK ((volstatus = ANY (ARRAY['Full'::text, 'Archive'::text, 'Append'::text, 'Recycle'::text, 'Purged'::text, 'Read-Only'::text, 'Disabled'::text, 'Error'::text, 'Busy'::text, 'Used'::text, 'Cleaning'::text, 'Scratch'::text]))) +); + + +ALTER TABLE public.media OWNER TO igny8; + +-- +-- Name: media_mediaid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.media_mediaid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.media_mediaid_seq OWNER TO igny8; + +-- +-- Name: media_mediaid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.media_mediaid_seq OWNED BY public.media.mediaid; + + +-- +-- Name: mediatype; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.mediatype ( + mediatypeid integer NOT NULL, + mediatype text NOT NULL, + readonly integer DEFAULT 0 +); + + +ALTER TABLE public.mediatype OWNER TO igny8; + +-- +-- Name: mediatype_mediatypeid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.mediatype_mediatypeid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.mediatype_mediatypeid_seq OWNER TO igny8; + +-- +-- Name: mediatype_mediatypeid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.mediatype_mediatypeid_seq OWNED BY public.mediatype.mediatypeid; + + +-- +-- Name: notifications_notification; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.notifications_notification ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + notification_type character varying(50) NOT NULL, + title character varying(200) NOT NULL, + message text NOT NULL, + severity character varying(20) NOT NULL, + object_id integer, + action_url character varying(500), + action_label character varying(50), + is_read boolean NOT NULL, + read_at timestamp with time zone, + metadata jsonb NOT NULL, + tenant_id bigint NOT NULL, + content_type_id integer, + site_id bigint, + user_id bigint, + CONSTRAINT notifications_notification_object_id_check CHECK ((object_id >= 0)) +); + + +ALTER TABLE public.notifications_notification OWNER TO igny8; + +-- +-- Name: notifications_notification_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.notifications_notification ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.notifications_notification_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: path; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.path ( + pathid integer NOT NULL, + path text NOT NULL +); +ALTER TABLE ONLY public.path ALTER COLUMN path SET STATISTICS 1000; + + +ALTER TABLE public.path OWNER TO igny8; + +-- +-- Name: path_pathid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.path_pathid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.path_pathid_seq OWNER TO igny8; + +-- +-- Name: path_pathid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.path_pathid_seq OWNED BY public.path.pathid; + + +-- +-- Name: pathhierarchy; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.pathhierarchy ( + pathid integer NOT NULL, + ppathid integer NOT NULL +); + + +ALTER TABLE public.pathhierarchy OWNER TO igny8; + +-- +-- Name: pathvisibility; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.pathvisibility ( + pathid integer NOT NULL, + jobid integer NOT NULL, + size bigint DEFAULT 0, + files integer DEFAULT 0 +); + + +ALTER TABLE public.pathvisibility OWNER TO igny8; + +-- +-- Name: plugin_downloads; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.plugin_downloads ( + id bigint NOT NULL, + ip_address inet, + user_agent character varying(500) NOT NULL, + download_type character varying(20) NOT NULL, + created_at timestamp with time zone NOT NULL, + account_id bigint, + plugin_id bigint NOT NULL, + site_id bigint, + version_id bigint NOT NULL +); + + +ALTER TABLE public.plugin_downloads OWNER TO igny8; + +-- +-- Name: plugin_downloads_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.plugin_downloads ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.plugin_downloads_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: plugin_installations; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.plugin_installations ( + id bigint NOT NULL, + is_active boolean NOT NULL, + last_health_check timestamp with time zone, + health_status character varying(20) NOT NULL, + update_notified_at timestamp with time zone, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + plugin_id bigint NOT NULL, + site_id bigint NOT NULL, + current_version_id bigint, + pending_update_id bigint +); + + +ALTER TABLE public.plugin_installations OWNER TO igny8; + +-- +-- Name: plugin_installations_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.plugin_installations ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.plugin_installations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: plugin_versions; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.plugin_versions ( + id bigint NOT NULL, + version character varying(20) NOT NULL, + version_code integer, + status character varying(20) NOT NULL, + file_path character varying(500) NOT NULL, + file_size integer NOT NULL, + checksum character varying(64) NOT NULL, + changelog text NOT NULL, + min_api_version character varying(20) NOT NULL, + min_platform_version character varying(20) NOT NULL, + min_php_version character varying(10) NOT NULL, + created_at timestamp with time zone NOT NULL, + released_at timestamp with time zone, + force_update boolean NOT NULL, + plugin_id bigint NOT NULL +); + + +ALTER TABLE public.plugin_versions OWNER TO igny8; + +-- +-- Name: plugin_versions_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.plugin_versions ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.plugin_versions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: plugins; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.plugins ( + id bigint NOT NULL, + name character varying(100) NOT NULL, + slug character varying(50) NOT NULL, + platform character varying(20) NOT NULL, + description text NOT NULL, + homepage_url character varying(200) NOT NULL, + is_active boolean NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.plugins OWNER TO igny8; + +-- +-- Name: plugins_id_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +ALTER TABLE public.plugins ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( + SEQUENCE NAME public.plugins_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: pool; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.pool ( + poolid integer NOT NULL, + name text NOT NULL, + numvols integer DEFAULT 0, + maxvols integer DEFAULT 0, + useonce smallint DEFAULT 0, + usecatalog smallint DEFAULT 0, + acceptanyvolume smallint DEFAULT 0, + volretention bigint DEFAULT 0, + voluseduration bigint DEFAULT 0, + maxvoljobs integer DEFAULT 0, + maxvolfiles integer DEFAULT 0, + maxvolbytes bigint DEFAULT 0, + autoprune smallint DEFAULT 0, + recycle smallint DEFAULT 0, + actiononpurge smallint DEFAULT 0, + pooltype text, + labeltype integer DEFAULT 0, + labelformat text NOT NULL, + enabled smallint DEFAULT 1, + scratchpoolid integer DEFAULT 0, + recyclepoolid integer DEFAULT 0, + nextpoolid integer DEFAULT 0, + migrationhighbytes bigint DEFAULT 0, + migrationlowbytes bigint DEFAULT 0, + migrationtime bigint DEFAULT 0, + CONSTRAINT pool_pooltype_check CHECK ((pooltype = ANY (ARRAY['Backup'::text, 'Copy'::text, 'Cloned'::text, 'Archive'::text, 'Migration'::text, 'Scratch'::text]))) +); + + +ALTER TABLE public.pool OWNER TO igny8; + +-- +-- Name: pool_poolid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.pool_poolid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.pool_poolid_seq OWNER TO igny8; + +-- +-- Name: pool_poolid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.pool_poolid_seq OWNED BY public.pool.poolid; + + +-- +-- Name: restoreobject; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.restoreobject ( + restoreobjectid integer NOT NULL, + objectname text NOT NULL, + restoreobject bytea NOT NULL, + pluginname text NOT NULL, + objectlength integer DEFAULT 0, + objectfulllength integer DEFAULT 0, + objectindex integer DEFAULT 0, + objecttype integer DEFAULT 0, + fileindex integer DEFAULT 0, + jobid integer, + objectcompression integer DEFAULT 0 +); + + +ALTER TABLE public.restoreobject OWNER TO igny8; + +-- +-- Name: restoreobject_restoreobjectid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.restoreobject_restoreobjectid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.restoreobject_restoreobjectid_seq OWNER TO igny8; + +-- +-- Name: restoreobject_restoreobjectid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.restoreobject_restoreobjectid_seq OWNED BY public.restoreobject.restoreobjectid; + + +-- +-- Name: snapshot; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.snapshot ( + snapshotid integer NOT NULL, + name text NOT NULL, + jobid integer DEFAULT 0, + filesetid integer DEFAULT 0, + createtdate bigint DEFAULT 0, + createdate timestamp without time zone NOT NULL, + clientid integer DEFAULT 0, + volume text NOT NULL, + device text NOT NULL, + type text NOT NULL, + retention integer DEFAULT 0, + comment text +); + + +ALTER TABLE public.snapshot OWNER TO igny8; + +-- +-- Name: snapshot_snapshotid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.snapshot_snapshotid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.snapshot_snapshotid_seq OWNER TO igny8; + +-- +-- Name: snapshot_snapshotid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.snapshot_snapshotid_seq OWNED BY public.snapshot.snapshotid; + + +-- +-- Name: status; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.status ( + jobstatus character(1) NOT NULL, + jobstatuslong text, + severity integer +); + + +ALTER TABLE public.status OWNER TO igny8; + +-- +-- Name: storage; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.storage ( + storageid integer NOT NULL, + name text NOT NULL, + autochanger integer DEFAULT 0 +); + + +ALTER TABLE public.storage OWNER TO igny8; + +-- +-- Name: storage_storageid_seq; Type: SEQUENCE; Schema: public; Owner: igny8 +-- + +CREATE SEQUENCE public.storage_storageid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.storage_storageid_seq OWNER TO igny8; + +-- +-- Name: storage_storageid_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: igny8 +-- + +ALTER SEQUENCE public.storage_storageid_seq OWNED BY public.storage.storageid; + + +-- +-- Name: unsavedfiles; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.unsavedfiles ( + unsavedid integer NOT NULL, + jobid integer NOT NULL, + pathid integer NOT NULL, + filenameid integer NOT NULL +); + + +ALTER TABLE public.unsavedfiles OWNER TO igny8; + +-- +-- Name: version; Type: TABLE; Schema: public; Owner: igny8 +-- + +CREATE TABLE public.version ( + versionid integer NOT NULL +); + + +ALTER TABLE public.version OWNER TO igny8; + +-- +-- Name: basefiles baseid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.basefiles ALTER COLUMN baseid SET DEFAULT nextval('public.basefiles_baseid_seq'::regclass); + + +-- +-- Name: client clientid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.client ALTER COLUMN clientid SET DEFAULT nextval('public.client_clientid_seq'::regclass); + + +-- +-- Name: device deviceid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.device ALTER COLUMN deviceid SET DEFAULT nextval('public.device_deviceid_seq'::regclass); + + +-- +-- Name: file fileid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.file ALTER COLUMN fileid SET DEFAULT nextval('public.file_fileid_seq'::regclass); + + +-- +-- Name: filename filenameid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.filename ALTER COLUMN filenameid SET DEFAULT nextval('public.filename_filenameid_seq'::regclass); + + +-- +-- Name: fileset filesetid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.fileset ALTER COLUMN filesetid SET DEFAULT nextval('public.fileset_filesetid_seq'::regclass); + + +-- +-- Name: igny8_system_ai_settings id; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.igny8_system_ai_settings ALTER COLUMN id SET DEFAULT nextval('public.igny8_system_ai_settings_id_seq'::regclass); + + +-- +-- Name: job jobid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.job ALTER COLUMN jobid SET DEFAULT nextval('public.job_jobid_seq'::regclass); + + +-- +-- Name: jobmedia jobmediaid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.jobmedia ALTER COLUMN jobmediaid SET DEFAULT nextval('public.jobmedia_jobmediaid_seq'::regclass); + + +-- +-- Name: location locationid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.location ALTER COLUMN locationid SET DEFAULT nextval('public.location_locationid_seq'::regclass); + + +-- +-- Name: locationlog loclogid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.locationlog ALTER COLUMN loclogid SET DEFAULT nextval('public.locationlog_loclogid_seq'::regclass); + + +-- +-- Name: log logid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.log ALTER COLUMN logid SET DEFAULT nextval('public.log_logid_seq'::regclass); + + +-- +-- Name: media mediaid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.media ALTER COLUMN mediaid SET DEFAULT nextval('public.media_mediaid_seq'::regclass); + + +-- +-- Name: mediatype mediatypeid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.mediatype ALTER COLUMN mediatypeid SET DEFAULT nextval('public.mediatype_mediatypeid_seq'::regclass); + + +-- +-- Name: path pathid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.path ALTER COLUMN pathid SET DEFAULT nextval('public.path_pathid_seq'::regclass); + + +-- +-- Name: pool poolid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.pool ALTER COLUMN poolid SET DEFAULT nextval('public.pool_poolid_seq'::regclass); + + +-- +-- Name: restoreobject restoreobjectid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.restoreobject ALTER COLUMN restoreobjectid SET DEFAULT nextval('public.restoreobject_restoreobjectid_seq'::regclass); + + +-- +-- Name: snapshot snapshotid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.snapshot ALTER COLUMN snapshotid SET DEFAULT nextval('public.snapshot_snapshotid_seq'::regclass); + + +-- +-- Name: storage storageid; Type: DEFAULT; Schema: public; Owner: igny8 +-- + +ALTER TABLE ONLY public.storage ALTER COLUMN storageid SET DEFAULT nextval('public.storage_storageid_seq'::regclass); + + +-- +-- Data for Name: admin_interface_theme; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.admin_interface_theme (id, name, active, title, title_visible, logo, logo_visible, css_header_background_color, title_color, css_header_text_color, css_header_link_color, css_header_link_hover_color, css_module_background_color, css_module_text_color, css_module_link_color, css_module_link_hover_color, css_module_rounded_corners, css_generic_link_color, css_generic_link_hover_color, css_save_button_background_color, css_save_button_background_hover_color, css_save_button_text_color, css_delete_button_background_color, css_delete_button_background_hover_color, css_delete_button_text_color, list_filter_dropdown, related_modal_active, related_modal_background_color, related_modal_rounded_corners, logo_color, recent_actions_visible, favicon, related_modal_background_opacity, env_name, env_visible_in_header, env_color, env_visible_in_favicon, related_modal_close_button_visible, language_chooser_active, language_chooser_display, list_filter_sticky, form_pagination_sticky, form_submit_sticky, css_module_background_selected_color, css_module_link_selected_color, css) FROM stdin; +1 Django t Django administration t t #0C4B33 #F5DD5D #44B78B #FFFFFF #C9F0DD #44B78B #FFFFFF #FFFFFF #C9F0DD t #0C3C26 #156641 #0C4B33 #0C3C26 #FFFFFF #BA2121 #A41515 #FFFFFF t t #000000 t #FFFFFF t 0.3 t #E74C3C t t t code t f f #FFFFCC #FFFFFF +\. + + +-- +-- Data for Name: auth_group; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.auth_group (id, name) FROM stdin; +1 Content Manager +2 Billing Admin +3 Support Agent +\. + + +-- +-- Data for Name: auth_group_permissions; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.auth_group_permissions (id, group_id, permission_id) FROM stdin; +43 1 81 +44 1 82 +45 1 84 +46 1 85 +47 1 86 +48 1 88 +49 1 89 +50 1 90 +51 1 92 +52 1 69 +53 1 70 +54 1 72 +55 1 73 +56 1 74 +57 1 76 +58 1 77 +59 1 78 +60 1 80 +61 2 257 +62 2 258 +63 2 260 +64 2 259 +65 2 253 +66 2 254 +67 2 256 +68 2 255 +69 2 145 +70 2 146 +71 2 148 +72 2 147 +73 2 149 +74 2 150 +75 2 152 +76 2 151 +77 2 29 +78 2 30 +79 2 32 +80 2 31 +81 3 84 +82 3 88 +83 3 32 +84 3 52 +\. + + +-- +-- Data for Name: auth_permission; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.auth_permission (id, name, content_type_id, codename) FROM stdin; +1 Can add log entry 1 add_logentry +2 Can change log entry 1 change_logentry +3 Can delete log entry 1 delete_logentry +4 Can view log entry 1 view_logentry +5 Can add permission 2 add_permission +6 Can change permission 2 change_permission +7 Can delete permission 2 delete_permission +8 Can view permission 2 view_permission +9 Can add group 3 add_group +10 Can change group 3 change_group +11 Can delete group 3 delete_group +12 Can view group 3 view_group +13 Can add content type 4 add_contenttype +14 Can change content type 4 change_contenttype +15 Can delete content type 4 delete_contenttype +16 Can view content type 4 view_contenttype +17 Can add session 5 add_session +18 Can change session 5 change_session +19 Can delete session 5 delete_session +20 Can view session 5 view_session +21 Can add plan 6 add_plan +22 Can change plan 6 change_plan +23 Can delete plan 6 delete_plan +24 Can view plan 6 view_plan +25 Can add user 7 add_user +26 Can change user 7 change_user +27 Can delete user 7 delete_user +28 Can view user 7 view_user +29 Can add Account 8 add_account +30 Can change Account 8 change_account +31 Can delete Account 8 delete_account +32 Can view Account 8 view_account +33 Can add Industry 9 add_industry +34 Can change Industry 9 change_industry +35 Can delete Industry 9 delete_industry +36 Can view Industry 9 view_industry +37 Can add Industry Sector 10 add_industrysector +38 Can change Industry Sector 10 change_industrysector +39 Can delete Industry Sector 10 delete_industrysector +40 Can view Industry Sector 10 view_industrysector +41 Can add password reset token 11 add_passwordresettoken +42 Can change password reset token 11 change_passwordresettoken +43 Can delete password reset token 11 delete_passwordresettoken +44 Can view password reset token 11 view_passwordresettoken +45 Can add Seed Keyword 12 add_seedkeyword +46 Can change Seed Keyword 12 change_seedkeyword +47 Can delete Seed Keyword 12 delete_seedkeyword +48 Can view Seed Keyword 12 view_seedkeyword +49 Can add site 13 add_site +50 Can change site 13 change_site +51 Can delete site 13 delete_site +52 Can view site 13 view_site +53 Can add sector 14 add_sector +54 Can change sector 14 change_sector +55 Can delete sector 14 delete_sector +56 Can view sector 14 view_sector +57 Can add Site User Access 15 add_siteuseraccess +58 Can change Site User Access 15 change_siteuseraccess +59 Can delete Site User Access 15 delete_siteuseraccess +60 Can view Site User Access 15 view_siteuseraccess +61 Can add subscription 16 add_subscription +62 Can change subscription 16 change_subscription +63 Can delete subscription 16 delete_subscription +64 Can view subscription 16 view_subscription +65 Can add ai task log 17 add_aitasklog +66 Can change ai task log 17 change_aitasklog +67 Can delete ai task log 17 delete_aitasklog +68 Can view ai task log 17 view_aitasklog +69 Can add Keyword 18 add_keywords +70 Can change Keyword 18 change_keywords +71 Can delete Keyword 18 delete_keywords +72 Can view Keyword 18 view_keywords +73 Can add Cluster 19 add_clusters +74 Can change Cluster 19 change_clusters +75 Can delete Cluster 19 delete_clusters +76 Can view Cluster 19 view_clusters +77 Can add Content Idea 20 add_contentideas +78 Can change Content Idea 20 change_contentideas +79 Can delete Content Idea 20 delete_contentideas +80 Can view Content Idea 20 view_contentideas +81 Can add Content 21 add_content +82 Can change Content 21 change_content +83 Can delete Content 21 delete_content +84 Can view Content 21 view_content +85 Can add Task 22 add_tasks +86 Can change Task 22 change_tasks +87 Can delete Task 22 delete_tasks +88 Can view Task 22 view_tasks +89 Can add Image 23 add_images +90 Can change Image 23 change_images +91 Can delete Image 23 delete_images +92 Can view Image 23 view_images +93 Can add content taxonomy map 24 add_contenttaxonomymap +94 Can change content taxonomy map 24 change_contenttaxonomymap +95 Can delete content taxonomy map 24 delete_contenttaxonomymap +96 Can view content taxonomy map 24 view_contenttaxonomymap +97 Can add content cluster map 25 add_contentclustermap +98 Can change content cluster map 25 change_contentclustermap +99 Can delete content cluster map 25 delete_contentclustermap +100 Can view content cluster map 25 view_contentclustermap +101 Can add content attribute map 26 add_contentattributemap +102 Can change content attribute map 26 change_contentattributemap +103 Can delete content attribute map 26 delete_contentattributemap +104 Can view content attribute map 26 view_contentattributemap +105 Can add system settings 27 add_systemsettings +106 Can change system settings 27 change_systemsettings +107 Can delete system settings 27 delete_systemsettings +108 Can view system settings 27 view_systemsettings +109 Can add account settings 28 add_accountsettings +110 Can change account settings 28 change_accountsettings +111 Can delete account settings 28 delete_accountsettings +112 Can view account settings 28 view_accountsettings +113 Can add ai prompt 29 add_aiprompt +114 Can change ai prompt 29 change_aiprompt +115 Can delete ai prompt 29 delete_aiprompt +116 Can view ai prompt 29 view_aiprompt +117 Can add ai settings 30 add_aisettings +118 Can change ai settings 30 change_aisettings +119 Can delete ai settings 30 delete_aisettings +120 Can view ai settings 30 view_aisettings +121 Can add Author Profile 31 add_authorprofile +122 Can change Author Profile 31 change_authorprofile +123 Can delete Author Profile 31 delete_authorprofile +124 Can view Author Profile 31 view_authorprofile +125 Can add integration settings 32 add_integrationsettings +126 Can change integration settings 32 change_integrationsettings +127 Can delete integration settings 32 delete_integrationsettings +128 Can view integration settings 32 view_integrationsettings +129 Can add module enable settings 33 add_moduleenablesettings +130 Can change module enable settings 33 change_moduleenablesettings +131 Can delete module enable settings 33 delete_moduleenablesettings +132 Can view module enable settings 33 view_moduleenablesettings +133 Can add module settings 34 add_modulesettings +134 Can change module settings 34 change_modulesettings +135 Can delete module settings 34 delete_modulesettings +136 Can view module settings 34 view_modulesettings +137 Can add Strategy 35 add_strategy +138 Can change Strategy 35 change_strategy +139 Can delete Strategy 35 delete_strategy +140 Can view Strategy 35 view_strategy +141 Can add user settings 36 add_usersettings +142 Can change user settings 36 change_usersettings +143 Can delete user settings 36 delete_usersettings +144 Can view user settings 36 view_usersettings +145 Can add credit transaction 37 add_credittransaction +146 Can change credit transaction 37 change_credittransaction +147 Can delete credit transaction 37 delete_credittransaction +148 Can view credit transaction 37 view_credittransaction +149 Can add credit usage log 38 add_creditusagelog +150 Can change credit usage log 38 change_creditusagelog +151 Can delete credit usage log 38 delete_creditusagelog +152 Can view credit usage log 38 view_creditusagelog +153 Can add Automation Rule 39 add_automationrule +154 Can change Automation Rule 39 change_automationrule +155 Can delete Automation Rule 39 delete_automationrule +156 Can view Automation Rule 39 view_automationrule +157 Can add Scheduled Task 40 add_scheduledtask +158 Can change Scheduled Task 40 change_scheduledtask +159 Can delete Scheduled Task 40 delete_scheduledtask +160 Can view Scheduled Task 40 view_scheduledtask +161 Can add Audience Profile 41 add_audienceprofile +162 Can change Audience Profile 41 change_audienceprofile +163 Can delete Audience Profile 41 delete_audienceprofile +164 Can view Audience Profile 41 view_audienceprofile +165 Can add Brand Personality 42 add_brandpersonality +166 Can change Brand Personality 42 change_brandpersonality +167 Can delete Brand Personality 42 delete_brandpersonality +168 Can view Brand Personality 42 view_brandpersonality +169 Can add Business Type 43 add_businesstype +170 Can change Business Type 43 change_businesstype +171 Can delete Business Type 43 delete_businesstype +172 Can view Business Type 43 view_businesstype +173 Can add Hero Imagery Direction 44 add_heroimagerydirection +174 Can change Hero Imagery Direction 44 change_heroimagerydirection +175 Can delete Hero Imagery Direction 44 delete_heroimagerydirection +176 Can view Hero Imagery Direction 44 view_heroimagerydirection +177 Can add Site Blueprint 45 add_siteblueprint +178 Can change Site Blueprint 45 change_siteblueprint +179 Can delete Site Blueprint 45 delete_siteblueprint +180 Can view Site Blueprint 45 view_siteblueprint +181 Can add Page Blueprint 46 add_pageblueprint +182 Can change Page Blueprint 46 change_pageblueprint +183 Can delete Page Blueprint 46 delete_pageblueprint +184 Can view Page Blueprint 46 view_pageblueprint +185 Can add Site Blueprint Cluster 47 add_siteblueprintcluster +186 Can change Site Blueprint Cluster 47 change_siteblueprintcluster +187 Can delete Site Blueprint Cluster 47 delete_siteblueprintcluster +188 Can view Site Blueprint Cluster 47 view_siteblueprintcluster +189 Can add Site Blueprint Taxonomy 48 add_siteblueprinttaxonomy +190 Can change Site Blueprint Taxonomy 48 change_siteblueprinttaxonomy +191 Can delete Site Blueprint Taxonomy 48 delete_siteblueprinttaxonomy +192 Can view Site Blueprint Taxonomy 48 view_siteblueprinttaxonomy +193 Can add Optimization Task 49 add_optimizationtask +273 Can add Theme 69 add_theme +194 Can change Optimization Task 49 change_optimizationtask +195 Can delete Optimization Task 49 delete_optimizationtask +196 Can view Optimization Task 49 view_optimizationtask +197 Can add deployment record 50 add_deploymentrecord +198 Can change deployment record 50 change_deploymentrecord +199 Can delete deployment record 50 delete_deploymentrecord +200 Can view deployment record 50 view_deploymentrecord +201 Can add publishing record 51 add_publishingrecord +202 Can change publishing record 51 change_publishingrecord +203 Can delete publishing record 51 delete_publishingrecord +204 Can view publishing record 51 view_publishingrecord +205 Can add site integration 52 add_siteintegration +206 Can change site integration 52 change_siteintegration +207 Can delete site integration 52 delete_siteintegration +208 Can view site integration 52 view_siteintegration +221 Can add Content Attribute 56 add_contentattribute +222 Can change Content Attribute 56 change_contentattribute +223 Can delete Content Attribute 56 delete_contentattribute +224 Can view Content Attribute 56 view_contentattribute +225 Can add Content Taxonomy 57 add_contenttaxonomy +226 Can change Content Taxonomy 57 change_contenttaxonomy +227 Can delete Content Taxonomy 57 delete_contenttaxonomy +228 Can view Content Taxonomy 57 view_contenttaxonomy +229 Can add content taxonomy relation 58 add_contenttaxonomyrelation +230 Can change content taxonomy relation 58 change_contenttaxonomyrelation +231 Can delete content taxonomy relation 58 delete_contenttaxonomyrelation +232 Can view content taxonomy relation 58 view_contenttaxonomyrelation +233 Can add Sync Event 59 add_syncevent +234 Can change Sync Event 59 change_syncevent +235 Can delete Sync Event 59 delete_syncevent +236 Can view Sync Event 59 view_syncevent +237 Can add automation config 60 add_automationconfig +238 Can change automation config 60 change_automationconfig +239 Can delete automation config 60 delete_automationconfig +240 Can view automation config 60 view_automationconfig +241 Can add automation run 61 add_automationrun +242 Can change automation run 61 change_automationrun +243 Can delete automation run 61 delete_automationrun +244 Can view automation run 61 view_automationrun +245 Can add Credit Cost Configuration 62 add_creditcostconfig +246 Can change Credit Cost Configuration 62 change_creditcostconfig +247 Can delete Credit Cost Configuration 62 delete_creditcostconfig +248 Can view Credit Cost Configuration 62 view_creditcostconfig +249 Can add credit package 63 add_creditpackage +250 Can change credit package 63 change_creditpackage +251 Can delete credit package 63 delete_creditpackage +252 Can view credit package 63 view_creditpackage +253 Can add invoice 64 add_invoice +254 Can change invoice 64 change_invoice +255 Can delete invoice 64 delete_invoice +256 Can view invoice 64 view_invoice +257 Can add payment 65 add_payment +258 Can change payment 65 change_payment +259 Can delete payment 65 delete_payment +260 Can view payment 65 view_payment +261 Can add Payment Method Configuration 66 add_paymentmethodconfig +262 Can change Payment Method Configuration 66 change_paymentmethodconfig +263 Can delete Payment Method Configuration 66 delete_paymentmethodconfig +264 Can view Payment Method Configuration 66 view_paymentmethodconfig +265 Can add account payment method 67 add_accountpaymentmethod +266 Can change account payment method 67 change_accountpaymentmethod +267 Can delete account payment method 67 delete_accountpaymentmethod +268 Can view account payment method 67 view_accountpaymentmethod +269 Can add Plan Limit Usage 68 add_planlimitusage +270 Can change Plan Limit Usage 68 change_planlimitusage +271 Can delete Plan Limit Usage 68 delete_planlimitusage +272 Can view Plan Limit Usage 68 view_planlimitusage +274 Can change Theme 69 change_theme +275 Can delete Theme 69 delete_theme +276 Can view Theme 69 view_theme +277 Can add task result 70 add_taskresult +278 Can change task result 70 change_taskresult +279 Can delete task result 70 delete_taskresult +280 Can view task result 70 view_taskresult +281 Can add chord counter 71 add_chordcounter +282 Can change chord counter 71 change_chordcounter +283 Can delete chord counter 71 delete_chordcounter +284 Can view chord counter 71 view_chordcounter +285 Can add group result 72 add_groupresult +286 Can change group result 72 change_groupresult +287 Can delete group result 72 delete_groupresult +288 Can view group result 72 view_groupresult +289 Can add historical Account 73 add_historicalaccount +290 Can change historical Account 73 change_historicalaccount +291 Can delete historical Account 73 delete_historicalaccount +292 Can view historical Account 73 view_historicalaccount +293 Can add historical payment 74 add_historicalpayment +294 Can change historical payment 74 change_historicalpayment +295 Can delete historical payment 74 delete_historicalpayment +296 Can view historical payment 74 view_historicalpayment +301 Can add Billing Configuration 76 add_billingconfiguration +297 Can add historical Credit Cost Configuration 75 add_historicalcreditcostconfig +298 Can change historical Credit Cost Configuration 75 change_historicalcreditcostconfig +299 Can delete historical Credit Cost Configuration 75 delete_historicalcreditcostconfig +300 Can view historical Credit Cost Configuration 75 view_historicalcreditcostconfig +389 Can add Account (Trash) 98 add_accounttrash +390 Can change Account (Trash) 98 change_accounttrash +391 Can delete Account (Trash) 98 delete_accounttrash +392 Can view Account (Trash) 98 view_accounttrash +393 Can add Sector (Trash) 99 add_sectortrash +394 Can change Sector (Trash) 99 change_sectortrash +395 Can delete Sector (Trash) 99 delete_sectortrash +396 Can view Sector (Trash) 99 view_sectortrash +397 Can add Site (Trash) 100 add_sitetrash +398 Can change Site (Trash) 100 change_sitetrash +399 Can delete Site (Trash) 100 delete_sitetrash +400 Can view Site (Trash) 100 view_sitetrash +401 Can add Cluster (Trash) 101 add_clusterstrash +402 Can change Cluster (Trash) 101 change_clusterstrash +403 Can delete Cluster (Trash) 101 delete_clusterstrash +404 Can view Cluster (Trash) 101 view_clusterstrash +405 Can add Content Idea (Trash) 102 add_contentideastrash +406 Can change Content Idea (Trash) 102 change_contentideastrash +407 Can delete Content Idea (Trash) 102 delete_contentideastrash +408 Can view Content Idea (Trash) 102 view_contentideastrash +409 Can add Keyword (Trash) 103 add_keywordstrash +410 Can change Keyword (Trash) 103 change_keywordstrash +411 Can delete Keyword (Trash) 103 delete_keywordstrash +412 Can view Keyword (Trash) 103 view_keywordstrash +414 Can change Content (Trash) 104 change_contenttrash +415 Can delete Content (Trash) 104 delete_contenttrash +416 Can view Content (Trash) 104 view_contenttrash +417 Can add Image (Trash) 105 add_imagestrash +418 Can change Image (Trash) 105 change_imagestrash +419 Can delete Image (Trash) 105 delete_imagestrash +420 Can view Image (Trash) 105 view_imagestrash +421 Can add Task (Trash) 106 add_taskstrash +422 Can change Task (Trash) 106 change_taskstrash +423 Can delete Task (Trash) 106 delete_taskstrash +424 Can view Task (Trash) 106 view_taskstrash +425 Can add Taxonomy (Trash) 107 add_contenttaxonomytrash +426 Can change Taxonomy (Trash) 107 change_contenttaxonomytrash +427 Can delete Taxonomy (Trash) 107 delete_contenttaxonomytrash +428 Can view Taxonomy (Trash) 107 view_contenttaxonomytrash +302 Can change Billing Configuration 76 change_billingconfiguration +303 Can delete Billing Configuration 76 delete_billingconfiguration +304 Can view Billing Configuration 76 view_billingconfiguration +305 Can add Global Integration Settings 77 add_globalintegrationsettings +306 Can change Global Integration Settings 77 change_globalintegrationsettings +307 Can delete Global Integration Settings 77 delete_globalintegrationsettings +308 Can view Global Integration Settings 77 view_globalintegrationsettings +309 Can add Account Integration Override 78 add_accountintegrationoverride +310 Can change Account Integration Override 78 change_accountintegrationoverride +311 Can delete Account Integration Override 78 delete_accountintegrationoverride +312 Can view Account Integration Override 78 view_accountintegrationoverride +313 Can add Global AI Prompt 79 add_globalaiprompt +314 Can change Global AI Prompt 79 change_globalaiprompt +315 Can delete Global AI Prompt 79 delete_globalaiprompt +316 Can view Global AI Prompt 79 view_globalaiprompt +317 Can add Global Author Profile 80 add_globalauthorprofile +318 Can change Global Author Profile 80 change_globalauthorprofile +319 Can delete Global Author Profile 80 delete_globalauthorprofile +320 Can view Global Author Profile 80 view_globalauthorprofile +321 Can add Global Strategy 81 add_globalstrategy +322 Can change Global Strategy 81 change_globalstrategy +323 Can delete Global Strategy 81 delete_globalstrategy +324 Can view Global Strategy 81 view_globalstrategy +325 Can add Global Module Settings 82 add_globalmodulesettings +326 Can change Global Module Settings 82 change_globalmodulesettings +327 Can delete Global Module Settings 82 delete_globalmodulesettings +328 Can view Global Module Settings 82 view_globalmodulesettings +329 Can add historical AI Model Configuration 83 add_historicalaimodelconfig +330 Can change historical AI Model Configuration 83 change_historicalaimodelconfig +331 Can delete historical AI Model Configuration 83 delete_historicalaimodelconfig +332 Can view historical AI Model Configuration 83 view_historicalaimodelconfig +333 Can add AI Model Configuration 84 add_aimodelconfig +334 Can change AI Model Configuration 84 change_aimodelconfig +335 Can delete AI Model Configuration 84 delete_aimodelconfig +336 Can view AI Model Configuration 84 view_aimodelconfig +337 Can add notification 85 add_notification +338 Can change notification 85 change_notification +339 Can delete notification 85 delete_notification +340 Can view notification 85 view_notification +341 Can add Image Prompt 86 add_imageprompts +342 Can change Image Prompt 86 change_imageprompts +343 Can delete Image Prompt 86 delete_imageprompts +344 Can view Image Prompt 86 view_imageprompts +345 Can add Publishing Settings 87 add_publishingsettings +346 Can change Publishing Settings 87 change_publishingsettings +347 Can delete Publishing Settings 87 delete_publishingsettings +348 Can view Publishing Settings 87 view_publishingsettings +349 Can add Integration Provider 88 add_integrationprovider +350 Can change Integration Provider 88 change_integrationprovider +351 Can delete Integration Provider 88 delete_integrationprovider +352 Can view Integration Provider 88 view_integrationprovider +353 Can add System AI Settings 89 add_systemaisettings +354 Can change System AI Settings 89 change_systemaisettings +355 Can delete System AI Settings 89 delete_systemaisettings +356 Can view System AI Settings 89 view_systemaisettings +357 Can add Webhook Event 90 add_webhookevent +358 Can change Webhook Event 90 change_webhookevent +359 Can delete Webhook Event 90 delete_webhookevent +360 Can view Webhook Event 90 view_webhookevent +361 Can add Email Template 91 add_emailtemplate +362 Can change Email Template 91 change_emailtemplate +363 Can delete Email Template 91 delete_emailtemplate +364 Can view Email Template 91 view_emailtemplate +365 Can add Email Log 92 add_emaillog +366 Can change Email Log 92 change_emaillog +367 Can delete Email Log 92 delete_emaillog +368 Can view Email Log 92 view_emaillog +369 Can add Email Settings 93 add_emailsettings +370 Can change Email Settings 93 change_emailsettings +371 Can delete Email Settings 93 delete_emailsettings +372 Can view Email Settings 93 view_emailsettings +373 Can add Plugin 94 add_plugin +374 Can change Plugin 94 change_plugin +375 Can delete Plugin 94 delete_plugin +376 Can view Plugin 94 view_plugin +377 Can add Plugin Version 95 add_pluginversion +378 Can change Plugin Version 95 change_pluginversion +379 Can delete Plugin Version 95 delete_pluginversion +380 Can view Plugin Version 95 view_pluginversion +381 Can add Plugin Installation 96 add_plugininstallation +382 Can change Plugin Installation 96 change_plugininstallation +383 Can delete Plugin Installation 96 delete_plugininstallation +384 Can view Plugin Installation 96 view_plugininstallation +385 Can add Plugin Download 97 add_plugindownload +386 Can change Plugin Download 97 change_plugindownload +387 Can delete Plugin Download 97 delete_plugindownload +388 Can view Plugin Download 97 view_plugindownload +413 Can add Content (Trash) 104 add_contenttrash +\. + + +-- +-- Data for Name: auth_user; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.auth_user (id, password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined) FROM stdin; +2 pbkdf2_sha256$1000000$xf5xMs5SEUAi9sQ8Wvw1cY$rLWE8cz1VxATgFnAtT+cCrBV0rgZ2aYkIf+L82Xjw+8= \N f admin f t 2025-10-29 19:13:20.076696+00 +1 pbkdf2_sha256$1000000$cI2wqK1ZC0KgAWACZyySfI$s3HVt4tDQa3ov6Ot14wKB75K0rG0quxRjV75cVNaMj0= 2025-10-29 19:30:07.60263+00 t root admin@igny8.com t t 2025-10-29 10:29:42.6185+00 +\. + + +-- +-- Data for Name: auth_user_groups; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.auth_user_groups (id, user_id, group_id) FROM stdin; +\. + + +-- +-- Data for Name: auth_user_user_permissions; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.auth_user_user_permissions (id, user_id, permission_id) FROM stdin; +\. + + +-- +-- Data for Name: basefiles; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.basefiles (baseid, jobid, fileid, fileindex, basejobid) FROM stdin; +\. + + +-- +-- Data for Name: billing_historicalaimodelconfig; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.billing_historicalaimodelconfig (id, model_name, display_name, model_type, provider, context_window, is_active, is_default, created_at, updated_at, history_id, history_date, history_change_reason, history_type, history_user_id, credits_per_image, quality_tier, tokens_per_credit, capabilities, cost_per_1k_input, cost_per_1k_output, max_tokens, landscape_size, square_size, valid_sizes) FROM stdin; +2 gpt-4.1 GPT-4.1 - Legacy Model text openai 8192 f f 2025-12-24 01:21:08.346229+00 2026-01-01 01:50:07.864045+00 1 2026-01-01 01:50:07.865765+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +9 gpt-image-1-mini GPT Image 1 Mini (Not compatible with OpenAI) image openai \N f f 2025-12-24 01:21:08.349347+00 2025-12-24 01:21:08.349352+00 2 2026-01-03 16:43:31.055272+00 \N - 3 \N \N \N {} \N \N \N \N 1024x1024 [] +8 gpt-image-1 GPT Image 1 (Not compatible with OpenAI) image openai \N f f 2025-12-24 01:21:08.348968+00 2025-12-24 01:21:08.348973+00 3 2026-01-03 16:43:31.063017+00 \N - 3 \N \N \N {} \N \N \N \N 1024x1024 [] +2 gpt-4.1 GPT-4.1 - Legacy Model text openai 8192 f f 2025-12-24 01:21:08.346229+00 2026-01-01 01:50:07.864045+00 4 2026-01-03 16:43:31.063976+00 \N - 3 \N \N \N {} \N \N \N \N 1024x1024 [] +5 gpt-5.2 GPT-5.2 - Most Advanced (16K context) text openai 16000 f t 2025-12-24 01:21:08.347511+00 2026-01-03 16:44:31.37767+00 5 2026-01-03 16:44:31.378757+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +11 civitai:618692@691639 Bria 3.2 - Premium image runware \N t t 2026-01-03 16:54:17.248193+00 2026-01-03 17:04:49.638569+00 6 2026-01-03 17:04:49.640122+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +4 gpt-5.1 GPT-5.1 - Premium text openai 8192 t f 2025-12-24 01:21:08.347068+00 2026-01-03 17:07:17.926681+00 7 2026-01-03 17:07:17.927544+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +11 civitai:618692@691639 Bria 3.2 - Premium image runware \N t t 2026-01-03 16:54:17.248193+00 2026-01-03 17:07:57.70515+00 8 2026-01-03 17:07:57.706033+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Standard image runware \N t f 2026-01-03 16:54:17.244617+00 2026-01-03 17:08:20.350575+00 9 2026-01-03 17:08:20.351581+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +11 bria:10@1 Bria 3.2 - Premium image runware \N t t 2026-01-03 16:54:17.248193+00 2026-01-03 18:53:33.490941+00 10 2026-01-03 18:53:33.49205+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +6 openai:2@3 DALL-E 3 - High Quality Images image runware \N t f 2025-12-24 01:21:08.347939+00 2026-01-03 19:03:03.501092+00 11 2026-01-03 19:03:03.502181+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +11 bria:10@1 Bria 3.2 - Premium image runware \N t t 2026-01-03 16:54:17.248193+00 2026-01-03 19:05:05.24953+00 12 2026-01-03 19:05:05.250437+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +7 dall-e-2 DALL-E 2 - Standard Quality image openai \N f f 2025-12-24 01:21:08.348489+00 2025-12-24 01:21:08.348496+00 13 2026-01-03 19:05:25.172549+00 \N - 3 \N \N \N {} \N \N \N \N 1024x1024 [] +6 google:4@2 Nano Banana Pro - High Accuracy & Quality image runware \N t f 2025-12-24 01:21:08.347939+00 2026-01-03 19:28:08.746058+00 14 2026-01-03 19:28:08.747467+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +11 bria:10@1 Bria 3.2 - Quality image runware \N t f 2026-01-03 16:54:17.248193+00 2026-01-03 20:11:36.60141+00 15 2026-01-03 20:11:36.603332+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-03 20:12:02.766414+00 16 2026-01-03 20:12:02.770239+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-03 20:12:02.845748+00 17 2026-01-03 20:12:02.846629+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-03 20:12:02.942077+00 18 2026-01-03 20:12:02.944038+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +6 google:4@2 Nano Banana - Premium image runware \N t f 2025-12-24 01:21:08.347939+00 2026-01-03 20:12:21.7245+00 19 2026-01-03 20:12:21.725564+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +5 dall-e-3 Dall-E-3 - Quality 2 image openai 16000 f t 2025-12-24 01:21:08.347511+00 2026-01-03 21:30:21.025451+00 20 2026-01-03 21:30:21.026459+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +5 dall-e-3 Dall-E-3 - Quality 2 image openai \N f t 2025-12-24 01:21:08.347511+00 2026-01-03 21:33:16.990345+00 21 2026-01-03 21:33:16.991184+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +5 dall-e-3 DALL-E 3 - HD Quality image openai \N t t 2025-12-24 01:21:08.347511+00 2026-01-03 21:36:28.706884+00 22 2026-01-03 21:36:28.708565+00 \N ~ \N \N \N \N {} \N \N \N \N 1024x1024 [] +5 dall-e-3 DALL-E 3 - HD Quality image openai \N t t 2025-12-24 01:21:08.347511+00 2026-01-03 21:46:19.717487+00 23 2026-01-03 21:46:19.719365+00 \N ~ \N \N \N \N {} \N \N \N \N 1024x1024 [] +11 bria:10@1 Bria 3.2 - Quality image runware \N t f 2026-01-03 16:54:17.248193+00 2026-01-03 20:11:36.60141+00 24 2026-01-03 23:04:24.326565+00 \N - 3 \N \N \N {} \N \N \N \N 1024x1024 [] +4 gpt-5.1 GPT-5.1 - Premium text openai 8192 t t 2025-12-24 01:21:08.347068+00 2026-01-03 23:49:12.914414+00 25 2026-01-03 23:49:12.91725+00 \N ~ 3 \N \N \N {} \N \N \N \N 1024x1024 [] +1 gpt-4o-mini GPT-4o mini - Fast & Affordable text openai 128000 t t 2025-12-24 01:21:08.345002+00 2026-01-04 22:12:40.728863+00 26 2026-01-04 22:12:40.731166+00 \N ~ 3 \N \N 10000 {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-04 22:12:51.037628+00 27 2026-01-04 22:12:51.0389+00 \N ~ 3 1 basic \N {} \N \N \N \N 1024x1024 [] +12 bytedance:seedream@4.5 Seedream 4.5 - High Quality image runware \N t f 2026-01-12 09:31:14.196836+00 2026-01-12 12:48:27.275538+00 28 2026-01-12 12:48:27.276616+00 \N ~ 3 5 quality_option2 \N {"high_resolution": true, "provider_settings": {"bytedance": {"maxSequentialImages": 4}}, "max_sequential_images": 4} \N \N \N 2560x1440 2048x2048 ["2048x2048", "2304x1728", "2560x1440", "1728x2304", "1440x2560"] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-12 17:45:40.029472+00 29 2026-01-12 17:45:40.030767+00 \N ~ 3 2 basic \N {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-12 17:45:50.652607+00 30 2026-01-12 17:45:50.653339+00 \N ~ 3 1 basic \N {} \N \N \N \N 1024x1024 [] +10 runware:97@1 Hi Dream Full - Basic image runware \N t t 2026-01-03 16:54:17.244617+00 2026-01-12 23:39:32.397493+00 32 2026-01-12 23:39:32.40071+00 \N ~ 3 1 basic \N {} \N \N \N \N 1024x1024 [] +12 bytedance:seedream@4.5 Seedream 4.5 - High Quality image runware \N t t 2026-01-12 09:31:14.196836+00 2026-01-12 18:26:59.096384+00 31 2026-01-12 18:26:59.09934+00 \N ~ 3 5 quality_option2 \N {"high_resolution": true, "provider_settings": {"bytedance": {"maxSequentialImages": 4}}, "max_sequential_images": 4} \N \N \N 2560x1440 2048x2048 ["2048x2048", "2304x1728", "2560x1440", "1728x2304", "1440x2560"] +\. + + +-- +-- Data for Name: billing_historicalcreditcostconfig; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.billing_historicalcreditcostconfig (operation_type, display_name, description, is_active, history_id, history_date, history_change_reason, history_type, history_user_id, calculation_mode, flat_rate_credits, per_item_credits, per_item_unit, base_credits) FROM stdin; +clustering Keyword Clustering AI-powered keyword clustering into semantic groups t 1 2025-12-23 23:29:27.418626+00 \N ~ \N flat_rate 10 \N 1 1 +idea_generation Content Ideas Generation Generate content ideas from keyword clusters t 2 2025-12-23 23:29:27.425173+00 \N ~ \N per_item \N 2.00 1 1 +content_generation Content Generation AI-powered article content generation t 3 2025-12-23 23:29:27.428321+00 \N ~ \N per_item \N 0.01 100 1 +image_generation Image Generation AI-powered image generation t 4 2025-12-23 23:29:27.431147+00 \N ~ \N per_image 5 \N 1 1 +linking Internal Linking AI-powered internal link suggestions t 5 2025-12-23 23:29:27.433676+00 \N ~ \N flat_rate 8 \N 1 1 +optimization Content Optimization AI-powered content optimization t 6 2025-12-23 23:29:27.43628+00 \N ~ \N per_item \N 0.01 100 1 +reparse Content Reparse Reparse existing content t 7 2025-12-23 23:29:27.438889+00 \N ~ \N flat_rate 1 \N 1 1 +clustering Keyword Clustering AI-powered keyword clustering into semantic groups t 18 2025-12-26 01:18:33.244487+00 \N ~ \N \N \N \N \N 1 +content_generation Content Generation AI-powered article content generation t 19 2025-12-26 01:20:20.506656+00 \N ~ 3 \N \N \N \N 1 +clustering Keyword Clustering AI-powered keyword clustering into semantic groups t 20 2025-12-26 01:20:46.535968+00 \N ~ 3 \N \N \N \N 1 +idea_generation Content Ideas Generation Generate content ideas from keyword clusters t 21 2025-12-26 01:21:06.611623+00 \N ~ 3 \N \N \N \N 1 +content_generation Content Generation AI-powered article content generation t 22 2025-12-26 01:21:21.256314+00 \N ~ 3 \N \N \N \N 1 +image_prompt_extraction Image Prompt Extraction Extract image prompts from content t 23 2025-12-26 01:21:41.147645+00 \N ~ 3 \N \N \N \N 1 +clustering Keyword Clustering AI-powered keyword clustering into semantic groups t 24 2025-12-29 03:09:43.43235+00 \N ~ 3 \N \N \N \N 1 +content_generation Content Generation AI-powered article content generation t 25 2025-12-29 03:10:00.837687+00 \N ~ 3 \N \N \N \N 1 +idea_generation Content Ideas Generation Generate content ideas from keyword clusters t 26 2025-12-29 03:10:18.181172+00 \N ~ 3 \N \N \N \N 1 +image_generation Image Generation AI-powered image generation t 27 2025-12-29 03:12:07.928966+00 \N ~ 3 \N \N \N \N 1 +image_prompt_extraction Image Prompt Extraction Extract image prompts from content t 28 2025-12-29 03:12:26.590518+00 \N ~ 3 \N \N \N \N 1 +\. + + +-- +-- Data for Name: billing_historicalpayment; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.billing_historicalpayment (id, amount, currency, status, payment_method, stripe_payment_intent_id, stripe_charge_id, paypal_order_id, paypal_capture_id, manual_reference, manual_notes, admin_notes, approved_at, processed_at, failed_at, refunded_at, failure_reason, metadata, created_at, updated_at, history_id, history_date, history_change_reason, history_type, tenant_id, approved_by_id, history_user_id, invoice_id) FROM stdin; +10 24742.00 PKR succeeded bank_transfer \N \N \N \N sdsdsdsd334 \N \N \N \N {"submitted_by": "axeeeeeee@cisidj.com"} 2025-12-09 06:07:28.263162+00 2025-12-09 06:13:29.345747+00 1 2026-01-07 03:32:35.610685+00 \N - 100 3 3 16 +8 139.00 USD completed bank_transfer \N \N \N \N 22334445 \N \N \N \N {"submitted_by": "paid1@paid.com"} 2025-12-09 01:41:54.56674+00 2025-12-09 01:48:27.145899+00 2 2026-01-07 03:32:35.617763+00 \N - 89 \N 3 10 +1 229.00 USD completed bank_transfer \N \N \N \N BANK-XFER-12345 Scale plan monthly payment \N 2025-12-08 11:00:01.961893+00 \N \N {} 2025-12-08 11:00:01.963732+00 2025-12-08 11:00:01.963741+00 3 2026-01-07 03:32:35.618776+00 \N - 14 \N 3 3 +11 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 04:58:47.928551+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmopVPdRe4dWeLw7A5CXUbZ", "checkout_session_id": "cs_test_b1uVXmv8vnFZ7LLTT8WBWBFT3MIiF0gipyApbVvIBfpCK4zXkyXcjMDqHP"} 2026-01-07 04:58:47.929119+00 2026-01-07 04:58:47.929125+00 4 2026-01-07 04:58:47.93552+00 \N + 111 \N \N 24 +11 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 04:58:47.928551+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmopVPdRe4dWeLw7A5CXUbZ", "checkout_session_id": "cs_test_b1uVXmv8vnFZ7LLTT8WBWBFT3MIiF0gipyApbVvIBfpCK4zXkyXcjMDqHP"} 2026-01-07 04:58:47.929119+00 2026-01-07 04:58:47.929125+00 5 2026-01-07 05:20:21.665022+00 \N - 111 \N 3 24 +12 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:23:02.997107+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpCyPdRe4dWeLwmKo2fjtN", "checkout_session_id": "cs_test_b1XmUhyWHmaSMjn7OtI7QBVsRYJQY4AxKZQLNvNwY3Jiti4HEqM47WCY7i"} 2026-01-07 05:23:02.997493+00 2026-01-07 05:23:02.997497+00 6 2026-01-07 05:23:02.99887+00 \N + 112 \N \N 26 +12 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:23:02.997107+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpCyPdRe4dWeLwmKo2fjtN", "checkout_session_id": "cs_test_b1XmUhyWHmaSMjn7OtI7QBVsRYJQY4AxKZQLNvNwY3Jiti4HEqM47WCY7i"} 2026-01-07 05:23:02.997493+00 2026-01-07 05:23:02.997497+00 7 2026-01-07 05:28:23.641541+00 \N - 112 \N 3 26 +13 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:29:52.676129+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpJaPdRe4dWeLwTYErRtZg", "checkout_session_id": "cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV"} 2026-01-07 05:29:52.676509+00 2026-01-07 05:29:52.676514+00 8 2026-01-07 05:29:52.677847+00 \N + 113 \N \N 27 +13 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:29:52.676129+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpJaPdRe4dWeLwTYErRtZg", "checkout_session_id": "cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV"} 2026-01-07 05:29:52.676509+00 2026-01-07 05:33:45.930199+00 9 2026-01-07 05:33:45.931685+00 \N ~ 113 3 3 27 +13 99.00 USD succeeded stripe \N \N \N \N 1233445 \N 2026-01-07 05:29:52.676129+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpJaPdRe4dWeLwTYErRtZg", "checkout_session_id": "cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV"} 2026-01-07 05:29:52.676509+00 2026-01-07 05:34:35.532394+00 10 2026-01-07 05:34:35.533822+00 \N ~ 113 3 3 27 +14 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:50:28.818573+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpdVPdRe4dWeLw3TnM7iX1", "checkout_session_id": "cs_test_b10ezXfIienpbt1TkiV6QHLfaOziixfiIsePLKuhwYm3BcwMzuSOHdXrHh"} 2026-01-07 05:50:28.818938+00 2026-01-07 05:50:28.818942+00 11 2026-01-07 05:50:28.820269+00 \N + 114 \N \N 28 +15 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:56:04.503919+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpiwPdRe4dWeLwgWJrtTvG", "checkout_session_id": "cs_test_b1JfqVEFg84dfp3ZzFu5zQZ5vRFbsRTPcvO9p9O3zbtiCfP6vC5fDQdEl5"} 2026-01-07 05:56:04.504419+00 2026-01-07 05:56:04.504429+00 12 2026-01-07 05:56:04.506551+00 \N + 115 \N \N 29 +13 99.00 USD succeeded stripe sub_sub_1SmpJaPdRe4dWeLwTYErRtZg cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV \N \N 1233445 \N 2026-01-07 05:29:52.676129+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpJaPdRe4dWeLwTYErRtZg", "checkout_session_id": "cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV"} 2026-01-07 05:29:52.676509+00 2026-01-07 06:06:48.565752+00 13 2026-01-07 06:06:48.567586+00 \N ~ 113 3 \N 27 +15 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:56:04.503919+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpiwPdRe4dWeLwgWJrtTvG", "checkout_session_id": "cs_test_b1JfqVEFg84dfp3ZzFu5zQZ5vRFbsRTPcvO9p9O3zbtiCfP6vC5fDQdEl5"} 2026-01-07 05:56:04.504419+00 2026-01-07 05:56:04.504429+00 14 2026-01-07 06:16:17.752151+00 \N - 115 \N 3 29 +14 99.00 USD succeeded stripe \N \N \N \N \N 2026-01-07 05:50:28.818573+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpdVPdRe4dWeLw3TnM7iX1", "checkout_session_id": "cs_test_b10ezXfIienpbt1TkiV6QHLfaOziixfiIsePLKuhwYm3BcwMzuSOHdXrHh"} 2026-01-07 05:50:28.818938+00 2026-01-07 05:50:28.818942+00 15 2026-01-07 06:16:17.879945+00 \N - 114 \N 3 28 +13 99.00 USD succeeded stripe sub_sub_1SmpJaPdRe4dWeLwTYErRtZg cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV \N \N 1233445 \N 2026-01-07 05:29:52.676129+00 \N \N {"plan_id": "2", "subscription_id": "sub_1SmpJaPdRe4dWeLwTYErRtZg", "checkout_session_id": "cs_test_b1aaB1bETuVOvDPdeeRQ9cDNeFGlJRCIJpYDhNRIfAON15xkpQifSC4QaV"} 2026-01-07 05:29:52.676509+00 2026-01-07 06:06:48.565752+00 16 2026-01-07 06:16:17.957088+00 \N - 113 3 3 27 +16 99.00 USD succeeded stripe sub_sub_1Smq6EPdRe4dWeLwVXaITQNe cs_test_b1E6SS441m26QYwfgkLXEZ7Hz72MXQqgCY194dU7dbDQfbqX73eMrHec3H \N \N \N 2026-01-07 06:20:08.565584+00 \N \N {"plan_id": "2", "payment_intent": null, "subscription_id": "sub_1Smq6EPdRe4dWeLwVXaITQNe", "checkout_session_id": "cs_test_b1E6SS441m26QYwfgkLXEZ7Hz72MXQqgCY194dU7dbDQfbqX73eMrHec3H"} 2026-01-07 06:20:08.566288+00 2026-01-07 06:20:08.566296+00 17 2026-01-07 06:20:08.567718+00 \N + 116 \N \N 30 +17 99.00 USD succeeded stripe sub_sub_1SmqguPdRe4dWeLwQ1NiRpmG cs_test_b1Noq1QcJ65vi2y8QAGNAO7gFB6qduciUvBHwB8ndglmADBuXl95P7xdj9 \N \N 2026-01-07 06:58:02.594331+00 2026-01-07 06:58:02.594327+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1SmqguPdRe4dWeLwQ1NiRpmG", "checkout_session_id": "cs_test_b1Noq1QcJ65vi2y8QAGNAO7gFB6qduciUvBHwB8ndglmADBuXl95P7xdj9"} 2026-01-07 06:58:02.594825+00 2026-01-07 06:58:02.594831+00 18 2026-01-07 06:58:02.596808+00 \N + 117 \N \N 31 +34 99.00 USD succeeded stripe sub_sub_1Sn3ppPdRe4dWeLwJ9oluGsL cs_test_b1Eok3UbgXFslugWFBDgPqbWzvNpwrSqbCj8vrTmJxN5ivFIC3rprt0Kyt \N \N \N 2026-01-07 21:00:08.029841+00 2026-01-07 21:00:08.029838+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn3ppPdRe4dWeLwJ9oluGsL", "checkout_session_id": "cs_test_b1Eok3UbgXFslugWFBDgPqbWzvNpwrSqbCj8vrTmJxN5ivFIC3rprt0Kyt"} 2026-01-07 21:00:08.030228+00 2026-01-07 21:00:08.030232+00 54 2026-01-12 11:23:29.303681+00 \N - 137 \N 3 51 +33 99.00 USD succeeded stripe sub_sub_1Sn35DPdRe4dWeLw1QTj5I7z cs_test_b1H8x9j1Oh5EZVlHRWyA9Pg8KriMpAKd70ZhPtakX8Req2IqOVdwQCcf7u \N \N \N 2026-01-07 20:11:57.963588+00 2026-01-07 20:11:57.963585+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn35DPdRe4dWeLw1QTj5I7z", "checkout_session_id": "cs_test_b1H8x9j1Oh5EZVlHRWyA9Pg8KriMpAKd70ZhPtakX8Req2IqOVdwQCcf7u"} 2026-01-07 20:11:57.963983+00 2026-01-07 20:11:57.963989+00 55 2026-01-12 11:24:44.315452+00 \N - 136 \N 3 50 +38 99.00 USD succeeded paypal \N \N 0TR53037EF4354343 4MB1074548728674L \N 2026-01-07 23:33:04.879912+00 2026-01-07 23:33:04.87991+00 \N \N {"plan_id": "2", "auto_approved": true, "subscription_type": "paypal_order"} 2026-01-07 23:33:04.880458+00 2026-01-07 23:33:04.880463+00 59 2026-01-12 11:33:34.71214+00 \N - 141 \N 3 55 +20 27522.00 PKR succeeded bank_transfer \N \N \N \N 112233344 2026-01-07 14:42:09.658497+00 2026-01-07 14:42:09.658502+00 \N \N {} 2026-01-07 14:04:55.681685+00 2026-01-07 14:42:09.658661+00 22 2026-01-07 14:42:09.659865+00 \N ~ 122 3 3 36 +20 27522.00 USD pending_approval bank_transfer \N \N \N \N 112233344 \N \N \N \N {} 2026-01-07 14:04:55.681685+00 2026-01-07 14:04:55.681696+00 21 2026-01-07 14:04:55.68754+00 \N + 122 \N \N 36 +18 299.00 USD succeeded paypal \N \N 01474504S6586113X 0C5077039A388543H 2026-01-07 08:31:36.339612+00 2026-01-07 08:31:36.339609+00 \N \N {"plan_id": "5", "auto_approved": true, "subscription_type": "paypal_order"} 2026-01-07 08:31:36.34005+00 2026-01-07 08:31:36.340055+00 19 2026-01-07 08:31:36.342593+00 \N + 119 \N \N 33 +39 99.00 USD pending_approval bank_transfer \N \N \N \N 11224455 \N \N \N \N {} 2026-01-08 01:49:54.731058+00 2026-01-08 01:49:54.731068+00 47 2026-01-08 02:39:57.856561+00 \N - 142 \N 3 56 +39 99.00 USD pending_approval bank_transfer \N \N \N \N 11224455 \N \N \N \N {} 2026-01-08 01:49:54.731058+00 2026-01-08 01:49:54.731068+00 46 2026-01-08 01:49:54.737281+00 \N + 142 \N \N 56 +31 199.00 USD succeeded stripe sub_sub_1Sn2UsPdRe4dWeLwmhE8rh69 cs_test_b1iPR6W3HeV58JsmbDv3W53atRuEiEA1wrSCNwKQ3sLFhZFnuuZBUHLAQ4 \N \N \N 2026-01-07 19:34:31.600476+00 2026-01-07 19:34:31.600474+00 \N \N {"plan_id": "4", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2UsPdRe4dWeLwmhE8rh69", "checkout_session_id": "cs_test_b1iPR6W3HeV58JsmbDv3W53atRuEiEA1wrSCNwKQ3sLFhZFnuuZBUHLAQ4"} 2026-01-07 19:34:31.600839+00 2026-01-07 19:34:31.600843+00 56 2026-01-12 11:24:56.908576+00 \N - 134 \N 3 48 +36 299.00 USD succeeded paypal \N \N 6SP712609W4179440 6UT198740L4677413 \N 2026-01-07 21:47:32.334483+00 2026-01-07 21:47:32.33448+00 \N \N {"plan_id": "5", "auto_approved": true, "subscription_type": "paypal_order"} 2026-01-07 21:47:32.334883+00 2026-01-07 21:47:32.334887+00 42 2026-01-07 21:47:32.335985+00 \N + 139 \N \N 53 +20 27522.00 PKR succeeded bank_transfer \N \N \N \N 112233344 2026-01-07 14:42:09.658497+00 2026-01-07 14:42:09.658502+00 \N \N {} 2026-01-07 14:04:55.681685+00 2026-01-07 14:42:09.658661+00 27 2026-01-07 17:34:15.82379+00 \N - 122 3 3 36 +18 299.00 USD succeeded paypal \N \N 01474504S6586113X 0C5077039A388543H \N 2026-01-07 08:31:36.339612+00 2026-01-07 08:31:36.339609+00 \N \N {"plan_id": "5", "auto_approved": true, "subscription_type": "paypal_order"} 2026-01-07 08:31:36.34005+00 2026-01-07 08:31:36.340055+00 28 2026-01-07 17:34:16.052671+00 \N - 119 \N 3 33 +17 99.00 USD succeeded stripe sub_sub_1SmqguPdRe4dWeLwQ1NiRpmG cs_test_b1Noq1QcJ65vi2y8QAGNAO7gFB6qduciUvBHwB8ndglmADBuXl95P7xdj9 \N \N \N 2026-01-07 06:58:02.594331+00 2026-01-07 06:58:02.594327+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1SmqguPdRe4dWeLwQ1NiRpmG", "checkout_session_id": "cs_test_b1Noq1QcJ65vi2y8QAGNAO7gFB6qduciUvBHwB8ndglmADBuXl95P7xdj9"} 2026-01-07 06:58:02.594825+00 2026-01-07 06:58:02.594831+00 29 2026-01-07 17:34:16.216807+00 \N - 117 \N 3 31 +16 99.00 USD succeeded stripe sub_sub_1Smq6EPdRe4dWeLwVXaITQNe cs_test_b1E6SS441m26QYwfgkLXEZ7Hz72MXQqgCY194dU7dbDQfbqX73eMrHec3H \N \N \N \N 2026-01-07 06:20:08.565584+00 \N \N {"plan_id": "2", "payment_intent": null, "subscription_id": "sub_1Smq6EPdRe4dWeLwVXaITQNe", "checkout_session_id": "cs_test_b1E6SS441m26QYwfgkLXEZ7Hz72MXQqgCY194dU7dbDQfbqX73eMrHec3H"} 2026-01-07 06:20:08.566288+00 2026-01-07 06:20:08.566296+00 30 2026-01-07 17:34:16.290753+00 \N - 116 \N 3 30 +38 99.00 USD succeeded paypal \N \N 0TR53037EF4354343 4MB1074548728674L \N 2026-01-07 23:33:04.879912+00 2026-01-07 23:33:04.87991+00 \N \N {"plan_id": "2", "auto_approved": true, "subscription_type": "paypal_order"} 2026-01-07 23:33:04.880458+00 2026-01-07 23:33:04.880463+00 45 2026-01-07 23:33:04.882873+00 \N + 141 \N \N 55 +42 199.00 USD succeeded bank_transfer \N \N \N \N 112233 Bulk approved by dev@igny8.com 2026-01-08 04:11:40.079412+00 2026-01-08 04:11:40.079418+00 \N \N {} 2026-01-08 04:10:41.80769+00 2026-01-08 04:11:40.079529+00 60 2026-01-12 11:33:48.473494+00 \N - 145 3 3 59 +40 99.00 USD succeeded stripe sub_sub_1Sn9f1PdRe4dWeLwdb4UZg4U cs_test_b1IJRo36GNNtLxtGo84MNPT1EEr5ITcRdXgF5MJCNRdtDEBnAQYmGYda5y \N \N \N 2026-01-08 03:13:21.10696+00 2026-01-08 03:13:21.106957+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn9f1PdRe4dWeLwdb4UZg4U", "checkout_session_id": "cs_test_b1IJRo36GNNtLxtGo84MNPT1EEr5ITcRdXgF5MJCNRdtDEBnAQYmGYda5y"} 2026-01-08 03:13:21.107275+00 2026-01-08 03:13:21.107278+00 63 2026-01-12 11:37:54.565625+00 \N - 143 \N 3 57 +29 99.00 USD succeeded stripe sub_sub_1Sn2C1PdRe4dWeLwmo4UCqDt cs_test_b1Z2OzYHhp7YMGoPX8kerqoBwCxSDV3pIgZOm82TrDFQgp89aGIGkuwd2u \N \N \N 2026-01-07 19:14:56.197352+00 2026-01-07 19:14:56.197349+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2C1PdRe4dWeLwmo4UCqDt", "checkout_session_id": "cs_test_b1Z2OzYHhp7YMGoPX8kerqoBwCxSDV3pIgZOm82TrDFQgp89aGIGkuwd2u"} 2026-01-07 19:14:56.197706+00 2026-01-07 19:14:56.19771+00 35 2026-01-07 19:14:56.198334+00 \N + 132 \N \N 46 +30 99.00 USD succeeded stripe sub_sub_1Sn2EPPdRe4dWeLwQLpkU90x cs_test_b19w1GIMeLwaEPzVHAdyFo43GgifnVZczwJZ10kDwcRz7RK8P7QTorInHy \N \N \N 2026-01-07 19:17:23.365363+00 2026-01-07 19:17:23.365361+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2EPPdRe4dWeLwQLpkU90x", "checkout_session_id": "cs_test_b19w1GIMeLwaEPzVHAdyFo43GgifnVZczwJZ10kDwcRz7RK8P7QTorInHy"} 2026-01-07 19:17:23.365593+00 2026-01-07 19:17:23.365597+00 36 2026-01-07 19:17:23.366259+00 \N + 133 \N \N 47 +31 199.00 USD succeeded stripe sub_sub_1Sn2UsPdRe4dWeLwmhE8rh69 cs_test_b1iPR6W3HeV58JsmbDv3W53atRuEiEA1wrSCNwKQ3sLFhZFnuuZBUHLAQ4 \N \N \N 2026-01-07 19:34:31.600476+00 2026-01-07 19:34:31.600474+00 \N \N {"plan_id": "4", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2UsPdRe4dWeLwmhE8rh69", "checkout_session_id": "cs_test_b1iPR6W3HeV58JsmbDv3W53atRuEiEA1wrSCNwKQ3sLFhZFnuuZBUHLAQ4"} 2026-01-07 19:34:31.600839+00 2026-01-07 19:34:31.600843+00 37 2026-01-07 19:34:31.601372+00 \N + 134 \N \N 48 +32 299.00 USD succeeded stripe sub_sub_1Sn2lsPdRe4dWeLwXPc6wB0w cs_test_b1YwMtMnl402owL9Twoqz7btZyrvvwQu6qNMImENUwkHMDPvxayxBvlJ5j \N \N \N 2026-01-07 19:51:59.013709+00 2026-01-07 19:51:59.013706+00 \N \N {"plan_id": "5", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2lsPdRe4dWeLwXPc6wB0w", "checkout_session_id": "cs_test_b1YwMtMnl402owL9Twoqz7btZyrvvwQu6qNMImENUwkHMDPvxayxBvlJ5j"} 2026-01-07 19:51:59.014048+00 2026-01-07 19:51:59.014054+00 38 2026-01-07 19:51:59.014662+00 \N + 135 \N \N 49 +33 99.00 USD succeeded stripe sub_sub_1Sn35DPdRe4dWeLw1QTj5I7z cs_test_b1H8x9j1Oh5EZVlHRWyA9Pg8KriMpAKd70ZhPtakX8Req2IqOVdwQCcf7u \N \N \N 2026-01-07 20:11:57.963588+00 2026-01-07 20:11:57.963585+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn35DPdRe4dWeLw1QTj5I7z", "checkout_session_id": "cs_test_b1H8x9j1Oh5EZVlHRWyA9Pg8KriMpAKd70ZhPtakX8Req2IqOVdwQCcf7u"} 2026-01-07 20:11:57.963983+00 2026-01-07 20:11:57.963989+00 39 2026-01-07 20:11:57.964581+00 \N + 136 \N \N 50 +34 99.00 USD succeeded stripe sub_sub_1Sn3ppPdRe4dWeLwJ9oluGsL cs_test_b1Eok3UbgXFslugWFBDgPqbWzvNpwrSqbCj8vrTmJxN5ivFIC3rprt0Kyt \N \N \N 2026-01-07 21:00:08.029841+00 2026-01-07 21:00:08.029838+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn3ppPdRe4dWeLwJ9oluGsL", "checkout_session_id": "cs_test_b1Eok3UbgXFslugWFBDgPqbWzvNpwrSqbCj8vrTmJxN5ivFIC3rprt0Kyt"} 2026-01-07 21:00:08.030228+00 2026-01-07 21:00:08.030232+00 40 2026-01-07 21:00:08.031163+00 \N + 137 \N \N 51 +37 199.00 USD succeeded bank_transfer \N \N \N \N tnv-114455 2026-01-07 22:16:42.333937+00 2026-01-07 22:16:42.333945+00 \N \N {} 2026-01-07 21:57:11.447492+00 2026-01-07 22:16:42.334101+00 44 2026-01-07 22:16:42.33583+00 \N ~ 140 3 3 54 +37 199.00 USD pending_approval bank_transfer \N \N \N \N tnv-114455 \N \N \N \N {} 2026-01-07 21:57:11.447492+00 2026-01-07 21:57:11.447501+00 43 2026-01-07 21:57:11.451478+00 \N + 140 \N \N 54 +40 99.00 USD succeeded stripe sub_sub_1Sn9f1PdRe4dWeLwdb4UZg4U cs_test_b1IJRo36GNNtLxtGo84MNPT1EEr5ITcRdXgF5MJCNRdtDEBnAQYmGYda5y \N \N \N 2026-01-08 03:13:21.10696+00 2026-01-08 03:13:21.106957+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn9f1PdRe4dWeLwdb4UZg4U", "checkout_session_id": "cs_test_b1IJRo36GNNtLxtGo84MNPT1EEr5ITcRdXgF5MJCNRdtDEBnAQYmGYda5y"} 2026-01-08 03:13:21.107275+00 2026-01-08 03:13:21.107278+00 48 2026-01-08 03:13:21.1106+00 \N + 143 \N \N 57 +37 199.00 USD succeeded bank_transfer \N \N \N \N tnv-114455 2026-01-07 22:16:42.333937+00 2026-01-07 22:16:42.333945+00 \N \N {} 2026-01-07 21:57:11.447492+00 2026-01-07 22:16:42.334101+00 49 2026-01-08 03:38:15.120667+00 \N - 140 3 3 54 +41 299.00 USD succeeded stripe sub_sub_1SnA6APdRe4dWeLw4c5oqar3 cs_test_b1Bg42PLwcxtspBAeP80Py5ZoJrYkehFb8mdj7LxoD2YVuN4K4tLkJKdGv \N \N \N 2026-01-08 03:41:25.121114+00 2026-01-08 03:41:25.121111+00 \N \N {"plan_id": "5", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1SnA6APdRe4dWeLw4c5oqar3", "checkout_session_id": "cs_test_b1Bg42PLwcxtspBAeP80Py5ZoJrYkehFb8mdj7LxoD2YVuN4K4tLkJKdGv"} 2026-01-08 03:41:25.121479+00 2026-01-08 03:41:25.121483+00 50 2026-01-08 03:41:25.12229+00 \N + 144 \N \N 58 +42 199.00 USD succeeded bank_transfer \N \N \N \N 112233 Bulk approved by dev@igny8.com 2026-01-08 04:11:40.079412+00 2026-01-08 04:11:40.079418+00 \N \N {} 2026-01-08 04:10:41.80769+00 2026-01-08 04:11:40.079529+00 52 2026-01-08 04:11:40.080618+00 \N ~ 145 3 3 59 +41 299.00 USD succeeded stripe sub_sub_1SnA6APdRe4dWeLw4c5oqar3 cs_test_b1Bg42PLwcxtspBAeP80Py5ZoJrYkehFb8mdj7LxoD2YVuN4K4tLkJKdGv \N \N \N 2026-01-08 03:41:25.121114+00 2026-01-08 03:41:25.121111+00 \N \N {"plan_id": "5", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1SnA6APdRe4dWeLw4c5oqar3", "checkout_session_id": "cs_test_b1Bg42PLwcxtspBAeP80Py5ZoJrYkehFb8mdj7LxoD2YVuN4K4tLkJKdGv"} 2026-01-08 03:41:25.121479+00 2026-01-08 03:41:25.121483+00 53 2026-01-12 09:43:25.79927+00 \N - 144 \N 3 58 +36 299.00 USD succeeded paypal \N \N 6SP712609W4179440 6UT198740L4677413 \N 2026-01-07 21:47:32.334483+00 2026-01-07 21:47:32.33448+00 \N \N {"plan_id": "5", "auto_approved": true, "subscription_type": "paypal_order"} 2026-01-07 21:47:32.334883+00 2026-01-07 21:47:32.334887+00 57 2026-01-12 11:25:46.221113+00 \N - 139 \N 3 53 +32 299.00 USD succeeded stripe sub_sub_1Sn2lsPdRe4dWeLwXPc6wB0w cs_test_b1YwMtMnl402owL9Twoqz7btZyrvvwQu6qNMImENUwkHMDPvxayxBvlJ5j \N \N \N 2026-01-07 19:51:59.013709+00 2026-01-07 19:51:59.013706+00 \N \N {"plan_id": "5", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2lsPdRe4dWeLwXPc6wB0w", "checkout_session_id": "cs_test_b1YwMtMnl402owL9Twoqz7btZyrvvwQu6qNMImENUwkHMDPvxayxBvlJ5j"} 2026-01-07 19:51:59.014048+00 2026-01-07 19:51:59.014054+00 58 2026-01-12 11:25:46.281449+00 \N - 135 \N 3 49 +42 199.00 USD pending_approval bank_transfer \N \N \N \N 112233 \N \N \N \N {} 2026-01-08 04:10:41.80769+00 2026-01-08 04:10:41.807698+00 51 2026-01-08 04:10:41.809751+00 \N + 145 \N \N 59 +30 99.00 USD succeeded stripe sub_sub_1Sn2EPPdRe4dWeLwQLpkU90x cs_test_b19w1GIMeLwaEPzVHAdyFo43GgifnVZczwJZ10kDwcRz7RK8P7QTorInHy \N \N \N 2026-01-07 19:17:23.365363+00 2026-01-07 19:17:23.365361+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2EPPdRe4dWeLwQLpkU90x", "checkout_session_id": "cs_test_b19w1GIMeLwaEPzVHAdyFo43GgifnVZczwJZ10kDwcRz7RK8P7QTorInHy"} 2026-01-07 19:17:23.365593+00 2026-01-07 19:17:23.365597+00 61 2026-01-12 11:37:20.897779+00 \N - 133 \N 3 47 +29 99.00 USD succeeded stripe sub_sub_1Sn2C1PdRe4dWeLwmo4UCqDt cs_test_b1Z2OzYHhp7YMGoPX8kerqoBwCxSDV3pIgZOm82TrDFQgp89aGIGkuwd2u \N \N \N 2026-01-07 19:14:56.197352+00 2026-01-07 19:14:56.197349+00 \N \N {"plan_id": "2", "auto_approved": true, "payment_intent": null, "subscription_id": "sub_1Sn2C1PdRe4dWeLwmo4UCqDt", "checkout_session_id": "cs_test_b1Z2OzYHhp7YMGoPX8kerqoBwCxSDV3pIgZOm82TrDFQgp89aGIGkuwd2u"} 2026-01-07 19:14:56.197706+00 2026-01-07 19:14:56.19771+00 62 2026-01-12 11:37:20.899558+00 \N - 132 \N 3 46 +\. + + +-- +-- Data for Name: cdimages; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.cdimages (mediaid, lastburn) FROM stdin; +\. + + +-- +-- Data for Name: client; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.client (clientid, name, uname, autoprune, fileretention, jobretention) FROM stdin; +\. + + +-- +-- Data for Name: counters; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.counters (counter, minvalue, maxvalue, currentvalue, wrapcounter) FROM stdin; +\. + + +-- +-- Data for Name: device; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.device (deviceid, name, mediatypeid, storageid, devmounts, devreadbytes, devwritebytes, devreadbytessincecleaning, devwritebytessincecleaning, devreadtime, devwritetime, devreadtimesincecleaning, devwritetimesincecleaning, cleaningdate, cleaningperiod) FROM stdin; +\. + + +-- +-- Data for Name: django_admin_log; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.django_admin_log (id, action_time, object_id, object_repr, action_flag, change_message, content_type_id, user_id) FROM stdin; +443 2025-11-28 12:08:10.128222+00 41 Creating the perfect outdoor patio: design ideas and inspirations 2 [{"changed": {"fields": ["Status"]}}] 21 3 +444 2025-11-30 02:20:18.40655+00 76 [Site Builder] Contact Us 3 22 3 +445 2025-11-30 02:20:18.406604+00 75 [Site Builder] Blog 3 22 3 +446 2025-11-30 02:20:18.406616+00 74 [Site Builder] FAQ 3 22 3 +447 2025-11-30 02:22:03.872434+00 30 How to Put on a Duvet Cover the Easy Way 3 21 3 +448 2025-11-30 02:22:15.4642+00 33 Duvet Covers Guide – What are Duvet Covers, Best Materials, Purpose and USA Size Guide 3 21 3 +449 2025-11-30 02:22:43.404014+00 5 Comprehensive guide to kitchen remodeling: transforming your space 3 21 3 +450 2025-11-30 02:23:41.253067+00 76 [Site Builder] Contact Us 3 22 3 +451 2025-11-30 02:23:41.253102+00 75 [Site Builder] Blog 3 22 3 +452 2025-11-30 02:23:41.253113+00 74 [Site Builder] FAQ 3 22 3 +453 2025-11-30 02:50:43.122426+00 76 [Site Builder] Contact Us 3 22 3 +454 2025-11-30 02:50:43.122468+00 75 [Site Builder] Blog 3 22 3 +455 2025-11-30 02:50:43.122484+00 74 [Site Builder] FAQ 3 22 3 +456 2025-11-30 03:20:34.096461+00 388 wall art ideas 3 18 3 +457 2025-11-30 05:02:28.841079+00 70 Essential Lawn Mowing Tips for a Pristine Yard 3 22 3 +458 2025-11-30 05:02:28.84111+00 61 Comprehensive Guide to Kitchen Remodeling: Transforming Your Space 3 22 3 +459 2025-11-30 05:02:28.841122+00 60 Your Ultimate Home Repair Guide: DIY Solutions for Every Homeowner 3 22 3 +460 2025-11-30 05:03:41.620284+00 70 Essential Lawn Mowing Tips for a Pristine Yard 3 22 3 +461 2025-11-30 05:03:41.620323+00 61 Comprehensive Guide to Kitchen Remodeling: Transforming Your Space 3 22 3 +462 2025-11-30 05:03:41.620338+00 60 Your Ultimate Home Repair Guide: DIY Solutions for Every Homeowner 3 22 3 +463 2025-11-30 05:45:57.56936+00 70 Essential Lawn Mowing Tips for a Pristine Yard 3 22 3 +464 2025-11-30 05:45:57.569414+00 61 Comprehensive Guide to Kitchen Remodeling: Transforming Your Space 3 22 3 +465 2025-11-30 05:45:57.569428+00 60 Your Ultimate Home Repair Guide: DIY Solutions for Every Homeowner 3 22 3 +466 2025-11-30 07:17:31.661733+00 61 Comprehensive Guide to Kitchen Remodeling: Transforming Your Space 3 22 3 +467 2025-11-30 07:17:31.661771+00 60 Your Ultimate Home Repair Guide: DIY Solutions for Every Homeowner 3 22 3 +3122 2025-12-28 23:37:55.706506+00 647Stiff back after long commutes or desk work? A well-chosen back massager from Walmart can provide daily relief without expensive spa visits. Understanding how to navigate Walmart’s shelves, website, and pickup options lets you upgrade comfort in a single afternoon instead of waiting days.
\r\nWalmart is often the first place shoppers think of for a budget-friendly back massager, thanks to aggressive pricing and frequent rollbacks. When you specifically search “back massager Walmart,” you’ll usually see dozens of models ranging from $15 handheld units to $200 shiatsu chairs. Learning how to narrow choices by features, intensity, and availability prevents impulse buys that disappoint after one week.
\r\nBecause many shoppers search “back massager near me,” Walmart’s dense store network becomes a major advantage. Over 4,600 U.S. locations mean you can often reserve a unit online and pick it up the same day. Comparing in-store inventory with online-only models helps you decide whether speed, price, or advanced features matter most.
\r\nOnce you understand Walmart’s inventory filters, pickup windows, and return policy, buying a massager becomes much lower risk. You can test a unit for several evenings, track how your back responds, then exchange within the return window if pressure or heat levels feel wrong. This guide walks step-by-step through choosing, ordering, receiving, and safely using your new device.
\r\n\r\nChoosing Walmart for a back massager purchase makes sense when you balance price, convenience, and risk. Their scale allows negotiated pricing with brands like HoMedics, Sharper Image, and RENPHO, often 10–25% lower than specialty retailers. Because many shoppers already visit weekly for groceries, adding a massager to a pickup order saves separate trips and extra delivery fees.
\r\nWalmart’s everyday pricing on mid-range back massagers typically sits between $25 and $80, covering heated cushions, handheld percussion units, and compact shiatsu pillows. Rollback promotions can drop popular models by $10–$30 for several weeks. When you compare similar devices at pharmacy chains, you’ll often see 15–20% higher tags, especially on units with heat and multiple intensity settings.
\r\nWith thousands of stores across urban, suburban, and rural areas, Walmart makes it unusually easy to find a “Walmart back massager nearby” without driving more than 15–20 minutes. Many Supercenters open until 11 p.m. or 24 hours, letting shift workers shop after late shifts. Extended hours also help you return or exchange a device quickly if vibration strength or fit feels wrong.
\r\n\r\nWalking into a Walmart back massager in store aisle, you’ll typically see four main categories: handheld percussion, shiatsu cushions, vibrating seat covers, and compact massage pillows. Each style handles back tension differently, from deep percussive strikes to rolling kneading nodes. Understanding these mechanisms helps you match a device to muscle depth, sensitivity, and how long you’ll realistically use it daily.
\r\nHandheld percussion massagers from brands like Wahl and RENPHO often range from $25 to $60, delivering 2,000–3,200 percussions per minute. Shiatsu back cushions, which strap to chairs and offer rotating nodes plus heat, usually cost $60–$150. Simpler vibrating seat covers run $30–$70, prioritizing gentle stimulation over deep pressure for users sensitive to strong kneading.
\r\nShiatsu cushions use rotating nodes that mimic thumbs, ideal for knots near the spine but sometimes intense for bony frames. Handheld units let you target precise spots, though holding a 2–3 pound device for 15 minutes can fatigue wrists. Vibrating covers distribute mild vibration along the full back, better for relaxation while watching TV than for severe trigger points.
\r\n\r\nBefore driving across town, it’s smart to confirm whether your preferred back massager is actually sitting on a shelf. Walmart’s website and mobile app let you search “back massager Walmart,” then filter by your ZIP code to see which models are in stock. This saves wasted trips, especially when popular shiatsu cushions sell out during holiday or tax-refund seasons.
\r\nOn Walmart.com, choose a product, then click “Pickup” and enter your ZIP code to display store availability within roughly 25 miles. The app uses your phone’s location to show “Available today,” “Limited stock,” or “Out of stock” labels. Checking two or three nearby stores often reveals at least one with your preferred massager ready for same-day pickup.
\r\n"Limited stock" usually means fewer than five units remain, so waiting a full day can risk losing availability. If you’re comparing several models, add them to your cart and switch store locations within the app to see which branch offers the best combination of price and pickup timing. This approach is especially helpful when chasing a specific color, feature set, or rollback promotion.
\r\n\r\nWhen deciding whether to buy back massager at Walmart in-store or order an online-only model, you’re really weighing speed against selection. Stores usually stock mainstream devices under $100 that appeal to most shoppers. Online listings, however, can include hundreds of niche models with adjustable arms, extra-long power cords, or specialized attachments for athletes and chronic pain sufferers.
\r\nIn-store shelves might carry 10–20 distinct back massager SKUs, while Walmart.com often shows 200–400 options, including marketplace sellers. Online-only models sometimes offer better specs, like 8–10 massage heads or 10+ intensity levels, at similar prices. However, shipping can add $5–$10 if your order doesn’t hit the usual free-shipping threshold around $35–$50.
\r\n\r\n\r\nWhen you need immediate relief, prioritize in-store pickup; when you want very specific features, filter online-only listings and accept a short delivery wait.
\r\n
Buying in store lets you feel materials, check control layouts, and estimate weight by lifting demo units or sealed boxes. Online shopping instead relies on user reviews, where you can filter by 3-star ratings to uncover recurring complaints about noise or weak motors. Reading ten or more detailed reviews often reveals whether a device maintains performance beyond the first few weeks.
\r\n\r\nWalmart pickup back massager options help you avoid browsing crowded aisles while still getting the device the same day. After choosing a model online, you can select free store pickup, curbside pickup, or in some areas, same-day delivery. These services streamline the process, especially when you’re already scheduling grocery pickup or coordinating around work shifts and childcare.
\r\nThese fulfillment methods differ in speed, contact level, and potential fees. The table below compares common scenarios for a mid-range $60 back massager purchased on a weekday afternoon. Times and fees vary by region, but this gives a realistic sense of how quickly you can start using your new device.
\r\n| Method | Typical Fee | Order Cutoff Time | Estimated Availability | Best For |
|---|---|---|---|---|
| In-Store Pickup | $0 | 6 p.m. | 2–4 hours | Shoppers comfortable walking inside and checking other departments. |
| Curbside Pickup | $0 | 6 p.m. | 2–4 hours | Drivers wanting minimal contact and quick trunk loading. |
| Same-Day Delivery | $7–$10 | 3 p.m. | 3–6 hours | People with mobility limits or no car needing home drop-off. |
| Standard Shipping | $0 over $35 | 11 p.m. | 2–4 days | Non-urgent buyers prioritizing price over speed. |
| Express Shipping | $10–$15 | 11 a.m. | 1–2 days | Shoppers outside delivery zones needing faster arrival. |
Curbside pickup is particularly useful if you’re managing back pain that makes walking across a large store uncomfortable. You simply tap “I’m on my way” in the app and park in designated spots, letting associates load the box into your trunk. This minimizes lifting and twisting, which can aggravate sensitive lumbar or thoracic areas before you even start treatment.
\r\n\r\nKnowing Walmart’s pricing rules and return policies reduces anxiety about choosing the wrong massager intensity or size. Most electric back massagers fall under standard electronics returns, typically allowing around 30 days with a receipt. This window lets you test different heat levels, massage modes, and durations to see how your muscles respond over several evenings and weekends.
\r\nYou can usually return a gently used back massager within the policy window to the customer service desk, as long as all accessories and packaging are included. If the price drops within a short period, some stores may honor a price adjustment when you bring the receipt. Always check your local store’s posted rules, since marketplace sellers follow different return timelines.
\r\nWalmart’s optional protection plans, often underwritten by Allstate, can cover mechanical failures after the manufacturer warranty ends, usually at 10–20% of product price. For a $120 shiatsu cushion used 30 minutes daily, motors and heating elements face heavy wear. A $15–$20 plan may pay off if you’ve previously burned out cheaper devices within 18–24 months.
\r\n\r\nOnce you bring your back massager home, how you test it during the first week affects comfort and safety. Start with shorter sessions, around 10–15 minutes, and the lowest intensity setting. This gives muscles time to adapt and helps you notice any tingling, numbness, or delayed soreness that might signal pressure that’s too intense or poorly positioned.
\r\nPlace shiatsu cushions on sturdy chairs with straight backs, avoiding soft couches that let nodes press directly into vertebrae. For vibrating seat covers, secure straps tightly so pads don’t slide and concentrate pressure on small areas. Set a phone timer to avoid exceeding manufacturer-recommended session lengths, often 15–20 minutes, especially when using built-in heat around the lower back.
\r\n| Massager Type | Recommended Session Length | Ideal Frequency | Key Safety Tip | Common Mistake |
|---|---|---|---|---|
| Shiatsu Cushion | 15–20 minutes | 1–2 times daily | Use a towel layer if nodes feel too sharp. | Leaning full body weight directly onto rotating nodes. |
| Handheld Percussion | 5–10 minutes | Up to twice daily | Keep device moving, never on spine bones. | Holding in one spot, causing bruising or irritation. |
| Vibrating Seat Cover | 20–30 minutes | 1–3 times daily | Start without heat if you have reduced sensation. | Using for hours while watching TV without breaks. |
| Massage Pillow | 10–15 minutes | 1–2 times daily | Position slightly off-center from the spine. | Falling asleep with the device still running. |
| Heated Wrap | 15–20 minutes | As needed | Check skin every few minutes for redness. | Wrapping too tightly, restricting circulation. |
Consult your doctor before using any electric massager if you have conditions like osteoporosis, herniated discs, implanted devices, or uncontrolled diabetes. Children shouldn’t operate these units unsupervised, and pregnant users should avoid strong vibration or deep kneading over the lower back. Unplug devices immediately after use to reduce fire risk and prevent curious pets or kids from accidentally activating them.
1926 {} Back Massager Walmart Shopping Guide: In-Store and Pickup Back Massager Walmart Guide: In-Store & Pickup Learn how to buy a back massager at Walmart, compare in-store vs online, check local availability, and use pickup or same-day delivery. back massager walmart ["walmart back massager in store", "back massager near me", "buy back massager at walmart", "walmart pickup back massager", "walmart back massager nearby"] approved 2026-01-01 00:09:48.718473+00 2026-01-12 11:55:10.699964+00 igny8 \N 12921 https://massagersmart.com/shopping-guides/health-wellness/back-massager-walmart-shopping-guide-in-store-and-pickup/ {} [] 0 0 {} post [] {} 90 61 21 \N 426 article \N \N {"wordpress_term_ids": {"tags": [1666, 1664, 1659, 1667, 1661, 1662, 1663, 1665, 1669, 1668, 1660], "categories": [1673], "igny8_sectors": [1671], "igny8_clusters": [1670]}} \N \N \N f \N scheduled 2026-01-15 09:00:00+00 2026-01-12 11:55:14.936654+00 +213 2026-01-12 11:45:20.310289+00Back pain is a common issue that many face today, leading to discomfort and reduced quality of life.
Choosing the right back massager can significantly impact your pain management strategy. The best back massagers not only provide relief but also promote relaxation, making them essential tools in your wellness arsenal.
This guide dives deep into the types of back massagers available, their benefits, and how to select the perfect one for your needs.
Back pain can stem from various causes, including muscle strains, herniated discs, and poor posture. According to the American Chiropractic Association, back pain affects approximately 80% of adults at some point in their lives. Understanding these causes is crucial for effective pain management.
Muscle strains and sprains are among the most common causes of back pain, often resulting from heavy lifting or sudden awkward movements.
Chronic back pain persists for more than three months, while acute pain can last a few days to a few weeks. Differentiating between these types helps in selecting the appropriate treatments and interventions.
Back massagers provide numerous benefits that go beyond mere relaxation. They can alleviate discomfort through targeted pressure and movements, enhancing blood circulation and reducing muscle tension.
“Regular use of back massagers can lead to improved flexibility and range of motion, assisting in faster recovery from muscle fatigue.”
One of the primary roles of back massagers is to alleviate discomfort. By using various techniques such as kneading, tapping, and rolling, these devices can ease muscle tension effectively.
Enhanced relaxation is another significant benefit. The soothing vibrations or movements can help lower stress levels, promoting a sense of well-being and tranquility.
Back massagers come in various types, each designed to cater to specific needs and preferences. Understanding the differences can help you make an informed decision.
Choosing the right type can depend on personal preference and specific back issues, making it essential to explore options.
When selecting a back massager, several features can enhance your experience. Look for adjustable settings, portability, and ease of use to ensure you get the most out of your device.
| Feature | Description | Benefits | Examples |
|---|---|---|---|
| Heat Function | Provides warmth to relieve tension | Enhances relaxation and blood flow | Sunbeam Heating Pad |
| Portability | Lightweight and easy to transport | Can be used anywhere | HoMedics Back Massager |
| Adjustable Intensity | Customizable massage strength | Allows for personalized comfort | RENPHO Shiatsu Massager |
| Multiple Settings | Variety of massage techniques | Caters to different pain levels | Naipo Back Massager |
These features significantly impact the effectiveness and enjoyment of using a massager, making them worth considering before purchase.
For those who spend long hours sitting, specific back massagers designed for chair use can provide relief at work or home. These devices offer convenience and comfort, integrating easily into daily routines.
These options not only provide relief but also enhance the sitting experience, making them great investments for anyone with back pain.
The market offers a variety of back and neck massagers that cater to different preferences and pain levels. Here are some of the top-rated options available today:
| Product Name | Type | Price | Rating |
|---|---|---|---|
| RENPHO Neck and Shoulder Massager | Shiatsu | $59.99 | 4.5/5 |
| HoMedics Neck and Back Massager | Vibrating | $79.99 | 4.2/5 |
| Comfier Heated Back Massager | Heated Shiatsu | $99.99 | 4.6/5 |
| InvoSpa Back Massager | Deep Tissue | $49.99 | 4.8/5 |
These devices offer various features, from heat settings to portability, making them suitable for different user needs.
“Aim for 15-30 minutes of use per session, ensuring to stay hydrated afterward for optimal muscle recovery.”
Using your back massager consistently can lead to better results. Incorporate stretching and hydration into your routine to enhance the overall benefits.
User testimonials provide valuable insights into the effectiveness of back massagers. Many users report significant improvements in their pain levels and overall relaxation after regular use.
“I’ve tried multiple massagers, but the RENPHO has changed my life. I use it daily, and my back pain has reduced tremendously.”
These experiences highlight the importance of finding a massager that suits personal needs and preferences to achieve the best results.
In summary, back massagers can play a vital role in managing back pain and enhancing relaxation. With various types and features available, taking the time to find the right device can significantly improve your quality of life. Explore different options and make a choice that aligns with your needs for the best results.
860 {} Complete Guide to the Best Back Massagers for Pain Relief Best Back Massagers for Pain Relief & Relaxation Explore the best back massagers designed for pain relief. Discover top devices, features, and user tips for relaxation. best back massager ["best back massager for chair", "best back and neck massager", "back vibrating massager", "back roller massager"] approved 2026-01-12 11:45:20.31057+00 2026-01-12 11:55:09.671028+00 igny8 \N \N \N {} [] 0 0 {} page [] {} 90 61 21 \N 427 cluster_hub \N \N {} \N \N \N f \N scheduled 2026-01-15 18:00:00+00 2026-01-12 11:55:14.955893+00 +214 2026-01-12 11:45:52.246281+00Finding the right back massager can transform your relaxation routine and significantly alleviate pain.
\nChoosing the best back massager is essential for anyone seeking relief from discomfort. With various options available, understanding your personal needs is crucial for making an informed decision. This guide outlines key factors to consider when selecting a back massager, ensuring it meets your specific requirements.
\nWhether you experience chronic pain or just want to unwind, selecting the right device can enhance your overall well-being and comfort.
\n\nUnderstanding your specific areas of discomfort is the first step in choosing the best back massager. Assessing your pain points helps narrow down options that target those areas effectively.
\nBegin by identifying where you feel pain. Is it in your lower back, upper back, or neck? This knowledge is vital for selecting a device designed for those specific regions.
\nEvaluate the severity of your pain. Mild discomfort may require a gentle massager, while chronic pain might necessitate a more robust option. This distinction influences your choice significantly.
\n\nYour lifestyle plays a critical role in choosing the best back massager. Consider factors such as work habits, daily routines, and personal preferences when making your selection.
\nBy aligning your massager choice with your lifestyle, you ensure that you will use it consistently, maximizing its benefits.
\n\nThere are various types of back massagers, each designed with unique features. Understanding these differences helps you choose the best back massager for your needs.
\n| Type | Features | Price Range | Best For |
|---|---|---|---|
| Vibrating | Soothing vibrations, adjustable intensity | $30 - $150 | Relaxation, mild pain |
| Rolling | Deep tissue, kneading action | $50 - $200 | Chronic pain relief |
| Heat Massage | Combines warmth with massage | $40 - $180 | Muscle relaxation |
| Handheld | Portable, versatile usage | $25 - $100 | Targeted relief |
Understanding the specifications of each type allows you to make an educated choice that aligns with your pain relief goals.
\n\nEstablishing a budget is critical when selecting the best back massager. Prices can vary widely based on features and brand reputation.
\nTypically, back massagers range from $25 for basic models to $300 for advanced options. Consider how often you will use the device and the features you need.
\nInvesting in a higher-priced massager may yield better long-term benefits, particularly if you suffer from chronic pain. Evaluate the warranty and return policies as well.
\n\nUser feedback is invaluable when selecting a back massager. Reviews provide real experiences, helping you understand the performance and effectiveness of various models.
\n“I’ve tried several massagers, and the ones with multiple settings and heat features have been the most beneficial for my chronic pain.” - A satisfied user.\n
Check online retailers, manufacturer websites, and independent review sites to gather a variety of opinions. Look for models with consistently high ratings.
\nDon’t hesitate to ask friends or family for their experiences. Personal recommendations can guide you toward reliable choices.
\n\nAs you finalize your choice, keep in mind some essential tips to ensure you make the best decision for your needs.
\nBy following these guidelines, you’ll be equipped to select the best back massager that suits your individual preferences and pain relief requirements.
648 {} How to Choose the Best Back Massager for Your Needs Best Back Massager for Pain Relief & Relaxation Discover how to choose the best back massager tailored to your needs for effective pain relief and relaxation. Read our comprehensive guide. best back massager ["best back massager for chair", "back roller massager"] approved 2026-01-12 11:45:52.246481+00 2026-01-12 11:55:09.156832+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 427 guide_tutorial \N \N {} \N \N \N f \N scheduled 2026-01-16 09:00:00+00 2026-01-12 11:55:14.963674+00 +216 2026-01-12 11:46:47.863074+00Are you tired of constant back pain and looking for a solution? A high-quality back massager can be your key to relief and relaxation.
Choosing the best back massager is crucial for effective pain relief. In 2023, various models feature innovative technologies tailored to alleviate discomfort, promote circulation, and enhance relaxation. This guide will help you navigate through the best options available this year.
With advancements in massage technology, back massagers now offer a range of features. From rolling mechanisms to soothing vibrations, finding the right device can transform your self-care routine. Let’s explore the criteria for selecting the best back massagers on the market.
When evaluating back massagers, several key criteria come into play. These include the type of massage technology, ease of use, build quality, and price. Each of these factors significantly impacts the user experience and overall effectiveness of the massager.
Different massagers employ various technologies such as kneading, rolling, or vibrating. Kneading massagers mimic the action of a professional masseuse, while rolling devices provide a more gentle massage. Understanding these technologies helps in selecting a model that meets your specific needs.
Many back massagers are designed for user-friendliness. Features like remote controls or app integration enhance the user experience. A model that is easy to operate ensures you can enjoy your massage without frustration.
Here’s a curated list of the best back massagers for pain relief available in 2023. Each model has been evaluated based on performance, user satisfaction, and innovative features.
Let’s take a closer look at each of the top back massagers. This section will highlight their standout features, providing insights into what makes them the best choice for pain relief.
The TheraGun Elite uses advanced percussive therapy technology. It features a powerful motor that delivers up to 40 percussions per second. This model is known for its quiet operation and ergonomic design, making it easy to use on different muscle groups.
The Naipo Shiatsu Massager offers a 3D kneading massage experience. Its built-in heat function enhances comfort and helps to relieve muscle tension effectively. This model is portable, making it ideal for home or office use.
Understanding the advantages and disadvantages of each massager can help you make an informed decision. Here’s a summary of the pros and cons of the top models.
| Product | Pros | Cons | Price |
|---|---|---|---|
| TheraGun Elite | Highly effective, quiet | Expensive | $399 |
| Naipo Shiatsu Massager | Portable, heat function | Limited intensity settings | $79 |
| RENPHO Back Massager | Multiple settings, washable | Bulkier design | $99 |
| Comfier Massage Chair | Full-body, zero gravity | Requires space | $699 |
Each model has unique benefits and limitations, making it essential to consider your specific needs when selecting a back massager.
User reviews provide valuable insights into the effectiveness of each back massager. Ratings often reflect satisfaction levels and highlight specific features that users appreciate or find lacking.
“The TheraGun Elite has completely changed my post-workout recovery routine. It’s powerful yet gentle on my sore muscles!”
Many users praise the Naipo Shiatsu Massager for its affordability and effectiveness, especially for lower back pain. Conversely, some users express concerns about the weight of the RENPHO model, making it less portable.
In summary, each back massager reviewed offers unique features tailored for pain relief and relaxation. Depending on your specific needs, the TheraGun Elite is the best option for serious muscle recovery, while the Naipo Shiatsu is ideal for casual users.
Investing in a high-quality back massager can significantly improve your quality of life. Consider the features that resonate most with your needs and choose wisely based on user feedback and expert recommendations.
672 {} 10 Best Back Massagers for Pain Relief in 2023 Best Back Massagers for Pain Relief in 2023 Discover the top 10 back massagers of 2023 designed for pain relief and relaxation. Find the perfect fit for your needs today! best back massager ["best back and neck massager", "back roller massager", "back vibrating massager"] approved 2026-01-12 11:46:47.863326+00 2026-01-12 11:55:08.127614+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 427 top_listicle \N \N {} \N \N \N f \N scheduled 2026-01-16 18:00:00+00 2026-01-12 11:55:14.978116+00 +217 2026-01-12 23:40:40.341178+00With the rise of technology, many people are torn between traditional massage therapies and modern back massagers. Which option offers better relief and relaxation?
\nWhen it comes to managing back pain and relaxation, the debate between back massagers and traditional massage techniques is ongoing. While traditional massages provide a tactile, human touch, back massagers offer convenience and affordability. Understanding the benefits and drawbacks of each can help you make an informed decision.
\nThis article will delve into the mechanics of traditional massage, examine how back massagers function, and compare the two methods to determine which could be the better choice for you.
\nTraditional massage involves hands-on techniques to manipulate muscles and soft tissues, aiming to relieve tension, improve circulation, and promote relaxation. Techniques such as Swedish, deep tissue, and sports massage offer various benefits suited to different needs.
\nReceiving a traditional massage can significantly reduce stress and anxiety levels, as studies indicate that massage therapy lowers cortisol levels by up to 31%. Moreover, it enhances blood flow, thereby improving nutrient delivery to muscles and tissues.
\nThere are numerous massage styles, each tailored to specific needs:
\nTraditional massage not only provides physical relief but also emotional benefits, making it a holistic choice for many.
\nBack massagers utilize various technologies, including vibration, kneading, and heat, to replicate the effects of a traditional massage. Many models are designed to target specific areas of the back, such as the lower back, shoulders, or neck.
\nMost back massagers feature adjustable settings that allow users to customize their experience. For example, vibrating massagers use oscillation to stimulate muscles, while rolling massagers mimic the kneading techniques of human hands. The addition of heat can enhance relaxation by increasing blood flow.
\nBack massagers come in various forms, catering to different preferences:
\nBy utilizing these technologies, back massagers can deliver effective pain relief in the comfort of your own home.
\nOne of the primary advantages of back massagers is their convenience. Users can enjoy a massage whenever they wish without making appointments or incurring high costs. Additionally, these devices often allow for personalization, such as adjusting intensity and heat settings.
\nIn addition to convenience, back massagers offer several benefits:
\nThese benefits make back massagers attractive options for those seeking relief without the commitment of traditional massage therapy.
\nWhen comparing back massagers and traditional massages, it's essential to consider both the advantages and disadvantages of each method. Traditional massage provides a personal touch and can address complex issues, while back massagers offer convenience and affordability.
\nThe following table highlights the key differences between back massagers and traditional massage:
\n| Feature | Back Massagers | Traditional Massage |
|---|---|---|
| Cost | One-time purchase (avg. $100-$300) | $50-$150 per session |
| Convenience | Available 24/7 | Appointment required |
| Customization | Adjustable settings | Personalized by therapist |
| Experience | Mechanical | Human touch |
| Effectiveness | Varies by model | Generally highly effective |
Understanding these differences can assist in making a more informed choice based on individual preferences and needs.
\nCost is often a significant factor in deciding between back massagers and traditional massage therapy. While traditional massages can offer immediate benefits, their cumulative costs can accumulate quickly.
\nThe average cost of a traditional massage session ranges from $50 to $150, depending on the therapist's experience and location. In contrast, a quality back massager may cost between $100 and $300, but it is a one-time investment that can be used indefinitely.
\nLet's examine the cost implications over a year:
\n| Option | Monthly Cost | Annual Cost |
|---|---|---|
| Traditional Massage | $100 (2 sessions) | $1200 |
| Back Massager | $25 (after purchase) | $300 |
Clearly, back massagers present a more economical option in the long run, especially for regular users.
\nUser experiences can vary significantly between back massagers and traditional massage therapies. While some individuals prefer the human touch and personalized care of traditional massage, others appreciate the convenience of back massagers.
\nMany users of back massagers report satisfaction with their ability to ease muscle tension at home. In contrast, those who favor traditional massages often highlight the emotional connection and relief provided by a skilled therapist.
\n“Having a professional massage therapist work on my back always feels more effective than any machine I’ve used.” - A Traditional Massage Enthusiast\n
Ultimately, preferences for back massagers or traditional massages can depend on individual needs. Those seeking immediate relief may lean towards traditional methods, while users valuing convenience might prefer back massagers.
\nBoth back massagers and traditional massages offer distinct advantages and disadvantages. Traditional massages excel in providing a personal touch and immediate relief, while back massagers provide affordability and convenience.
\nWhen deciding, consider your individual needs, budget, and lifestyle. Those who prioritize regular pain management may find back massagers a practical choice, but for a more immersive experience, traditional massages may be worth the investment.
\nUltimately, the best choice depends on your unique preferences and circumstances.
977 {} Back Massagers vs. Traditional Massage: Which Is Better? Back Massagers vs. Traditional Massage - Find Out Now Explore the effectiveness of back massagers versus traditional massage techniques to discover which offers better pain relief and relaxation. back massagers ["best back massager", "back vibrating massager", "best back and neck massager"] published 2026-01-12 23:40:40.341703+00 2026-01-12 23:42:45.995761+00 igny8 \N 13011 https://massagersmart.com/health/wellness/back-massagers-vs-traditional-massage-which-is-better/ {} [] 0 0 {} post [] {} 90 61 21 \N 427 comparison \N \N {"wordpress_term_ids": {"tags": [1659, 1709, 1710, 1711, 1661, 1728, 1707, 1727], "categories": [1724], "igny8_sectors": [1671], "igny8_clusters": [1713]}} \N \N \N f \N published \N 2026-01-12 23:53:19.101536+00 +205 2026-01-01 00:06:52.412189+00Used correctly, a heated back massager can feel like having a therapist on call, melting stiffness in 10–20 minutes. Used carelessly, the same device can leave you with irritation, bruising, or even mild burns. Technique, timing, and settings matter far more than price or brand name.
\r\nA back massager with heat works by combining mechanical pressure, usually 20–40 percussions or kneads per second, with warmth around 38–45°C. Together, these increase local blood flow and temporarily relax tight muscle fibers. To get real relief, you must match settings to your body weight, sensitivity, and pain pattern instead of copying generic instructions.
\r\nMany people press too hard, run sessions beyond 20 minutes, or use maximum heat directly on bare skin, then blame the device for soreness. Understanding how heat therapy for back pain interacts with your circulation, nerves, and existing injuries helps you avoid those mistakes. A few simple rules can turn your massager into a safe, daily recovery tool.
\r\nThink of each session as part of a larger back-care plan, not a quick fix. When you pair correct positioning with stretching, ergonomic habits, and strength work, even an affordable back massager handheld model can support long-term relief rather than just masking discomfort for a few hours.
\r\n\r\nEvery heated back massager has its own combination of intensity levels, heat ranges, and massage modes, yet most users only try the default setting. Before you ever touch your spine, learn what each button changes. Intensity usually controls stroke depth in millimeters or percussions per minute, while heat settings adjust surface temperature by roughly 3–5°C increments.
\r\nMost massage gun for back devices offer at least three intensity levels, from around 1,200 to 3,200 percussions per minute. Lower settings suit bony, sensitive areas, while higher ones target dense lumbar muscles. Heat buttons often cycle between off, low, and high, roughly 38–45°C. Rotating or kneading modes change stroke patterns, which can reduce adaptation and keep tight areas responding.
\r\nAttachments change how force distributes across your tissues. A wide, flat head spreads pressure over 10–15 cm², ideal for broad lumbar muscles. A fork attachment straddles the spine, avoiding direct pressure on vertebrae or the sacrum. Ball heads suit general use, while bullet tips should rarely touch the back, because their tiny surface area concentrates force and increases bruising risk.
\r\n\r\n\r\n\r\nThink of settings as dials for depth, temperature, and area coverage. Combining low heat with moderate intensity on a wide head is usually safer than max heat and a narrow tip, especially around the spine.
\r\n
Preparation determines how your tissues respond to mechanical and thermal stress. Going straight from a cold office chair to a 45°C massager on maximum intensity shocks your muscles and blood vessels. Instead, give yourself at least five minutes to warm up with light movement, such as walking around your home or gently swinging your arms and shoulders.
\r\nUse thin, flexible clothing like a cotton T-shirt or light athletic top between the device and skin, especially for the first five minutes. This layer reduces hot spots and spreads heat more evenly. Drink 250–500 ml of water about 30 minutes before your session because better hydration improves tissue elasticity and reduces post-massage soreness in many people.
\r\nChoose a firm surface such as a supportive chair or floor mat rather than a soft bed, which lets your spine sag. Support your head with a pillow so your neck stays in a neutral position, not flexed or extended. If someone is helping, agree on a stop signal so they can immediately reduce pressure or move away from sensitive areas when needed.
\r\n\r\n\r\n\r\nA calm, stable setup does more than feel pleasant; it prevents you from bracing unconsciously, which can increase muscle tension by 10–20% during massage.
\r\n
Positioning determines whether force reaches tight muscles or lands on joints, ribs, or nerves. When learning how to use back massager tools, imagine a two-finger safety zone along your spine where direct pressure should be minimal. Aim the device slightly off center, into the thick muscle columns that run parallel to your vertebrae.
\r\nFor the upper back, sit upright against a chair with your shoulders relaxed and slightly rounded. Guide the back massager handheld head along the trapezius and rhomboids, about two finger-widths from the spine, moving slowly at roughly 2–3 centimeters per second. Avoid staying directly over the shoulder blades longer than 10 seconds, because bone reflects force and can cause lingering tenderness.
\r\nLower-back muscles often tolerate more pressure but are closer to sensitive joints and nerves. Recline slightly with a small pillow under your knees to flatten the lumbar curve. Angle the massage gun for back use downward into the thick erector muscles, never straight into the spine or sacrum. If self-reach is difficult, use a longer-handled model or ask a partner to maintain gentle, controlled movements.
\r\n\r\nTime and frequency make the difference between helpful stimulation and overuse. Most manufacturers recommend 10–20 minutes per region, yet many users unknowingly double that while watching TV. Heat magnifies the effect by dilating blood vessels, so your tissues may feel fine during the session but become irritated several hours later if exposure was excessive.
\r\n| Goal | Session Length per Area | Weekly Frequency | Typical Intensity Range | Heat Setting |
|---|---|---|---|---|
| General relaxation | 10–15 minutes | 3–5 times | Low–medium (1,200–2,000 ppm) | Low (38–40°C) |
| Chronic low back pain | 8–12 minutes | 5–7 times | Low–medium (1,200–2,000 ppm) | Low–medium (38–42°C) |
| Post-workout recovery | 5–10 minutes | 2–4 times | Medium–high (1,800–2,800 ppm) | Off–low (room–40°C) |
| Acute stiffness flare | 5–8 minutes | 1–2 times | Low (1,200–1,600 ppm) | Low (38–40°C) |
| Desk-job maintenance | 8–10 minutes | 5 times | Low–medium (1,200–2,000 ppm) | Off–low (room–40°C) |
Use these ranges as upper limits, not minimum targets. If your skin looks very red, feels hot for more than 20 minutes afterward, or muscles feel bruised the next day, shorten future sessions by 30–50%. Remember that different spinal regions count as separate areas, so a full back massage may involve three shorter segments rather than one long, continuous pass.
\r\n\r\nHeat and intensity are levers you adjust based on your goal, pain stage, and sensitivity. Higher intensity without heat mainly targets deep mechanical tension, while moderate heat with lower intensity emphasizes circulation and relaxation. Combining both at maximum levels is rarely necessary and often counterproductive, especially if you have reduced sensation or a history of skin issues.
\r\nFor post-workout recovery, keep heat off or low because already-warm tissues can become overly vasodilated, increasing inflammation. Chronic, dull low back pain often responds to 38–42°C heat with low intensity, applied 10 minutes nightly. For stress-related tightness, alternate one minute of light percussion with one minute of still, heated contact to encourage the nervous system to downshift.
\r\nAs your tissues adapt over two to four weeks, you may tolerate slightly higher intensity or longer sessions. However, changes should be gradual, around 10–20% increases in time or intensity, never both simultaneously. If pain spikes above 5 out of 10 during or after a session, revert to previous settings and consult a clinician before resuming more aggressive combinations.
\r\n\r\nSafe use of back massager devices means respecting both the power of mechanical force and the risks of excessive heat. Certain medical conditions, medications, and recent injuries change how your tissues handle pressure and temperature. Ignoring these factors can turn a simple wellness tool into a source of complications, especially around the spine and major blood vessels.
\r\nIf you have diabetes with neuropathy, you may not feel when temperatures exceed 42°C, raising burn risk. People on blood thinners like warfarin or apixaban bruise more easily from percussion, particularly around the ribs and lower back. Recent surgery, within six to twelve weeks, is another red flag; scar tissue and healing tissues do not tolerate aggressive vibration or heat.
\r\n\r\n\r\n\r\nWhen in doubt, treat heat and percussion like medication: adjust dose carefully, avoid contraindicated areas, and ask your clinician if you have complex conditions.
\r\n
A heated back massager works best as one part of a broader strategy that includes mobility, strength, and ergonomic changes. Think of it as a recovery amplifier, not the main treatment. Using it consistently for 8–12 minutes after specific activities, like long drives or desk work, prevents minor stiffness from accumulating into weeks of persistent pain.
\r\nAfter each session, follow up with two or three targeted stretches, such as child’s pose or a gentle seated twist, holding 20–30 seconds. This helps lock in the increased blood flow and tissue pliability you created. Adjust your workstation so your monitor is at eye level and your hips are slightly above knee height, reducing the load on lumbar discs throughout the day.
\r\nKeep a simple log for two weeks, noting session time, settings, and pain levels before and one hour after use. Patterns will show whether your current routine reduces pain by at least two points on a 0–10 scale or mainly offers short-term comfort. If benefits fade quickly, prioritize more frequent, shorter sessions and integrate core strengthening twice weekly to support lasting structural change.
\r\n\r\n1752 {} How to Use a Back Massager with Heat Safely and Effectively Use a Back Massager with Heat Safely & Effectively Learn how to use a back massager with heat safely, including positioning, timing, intensity, and heat therapy tips for lasting back pain relief. back massager with heat ["heated back massager", "how to use back massager", "back massager handheld", "massage gun for back", "safe use of back massager", "heat therapy for back"] approved 2026-01-01 00:06:52.412474+00 2026-01-12 11:55:12.248051+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 425 article \N \N {} \N \N \N f \N scheduled 2026-01-14 09:00:00+00 2026-01-12 11:55:14.906961+00 +206 2026-01-01 00:07:24.63013+00Long-term relief comes when massage, movement, and posture all point in the same direction; the device simply makes those other habits easier to maintain.
\r\n
Shopping for back relief can get confusing fast when you see long-handled vibrating wands beside chunky massage guns promising "deep tissue" results. Both claim to loosen knots and ease stiffness, yet they feel and work very differently in real life, especially on hard-to-reach areas of your upper and mid-back.
\r\nDeciding between a back massager handheld device and a back massager gun starts with understanding how you actually hurt. If your pain feels like surface-level tightness from hours at a desk, you likely need gentle vibration and heat. If you train hard or wake with deep, stabbing knots, you may benefit from stronger percussive force instead.
\r\nThe best back massager for you also depends on how often you will use it, who will operate it, and where. A 1.2-pound cordless massage gun fits easily in a gym bag, while a corded handheld with heat suits nightly use beside the couch. Matching design to lifestyle prevents expensive gadgets from gathering dust.
\r\nBudget plays a quieter but crucial role. Entry handheld models start under $40, while reputable massage guns begin around $80 and climb above $300. By the end of this comparison, you will know which category aligns with your back pain pattern, mobility, and spending comfort, so every session feels targeted rather than trial-and-error.
\r\n\r\nLong-handled back massagers use vibration or oscillation through a rounded head to relax muscles over a wider surface. Most models weigh 1.5–2.5 pounds and run at 2,000–3,500 vibrations per minute. Their design favors sweeping movements across the shoulders, upper back, and lower back, making them approachable for beginners and seniors.
\r\nMany handhelds, like the RENPHO corded back massager, include 3–6 interchangeable heads and 3–5 speed settings. Handles often measure 15–17 inches, allowing you to reach between shoulder blades without twisting. Curved grips with rubberized sections reduce slipping when your hands are slightly sweaty, which matters during 10–15 minute evening sessions.
\r\nBecause these devices distribute vibration over a broader area, the sensation feels more like a firm manual massage than a punching motion. This makes them less intimidating for users with fibromyalgia or chronic tension who react poorly to sudden impact. However, the energy diffuses quickly, so tight knots near the spine or shoulder blade edges may require several passes before releasing.
\r\n\r\n\r\nHandheld designs shine when you want relaxing, spa-like coverage over large back regions rather than pinpoint, athletic-style muscle work.
\r\n
Most people use a back massager handheld device in 10-minute blocks, two to three times daily during flare-ups. The corded versions plug into a wall near a couch or bed, encouraging consistent routines. Users typically start with a medium setting, then increase speed as tissues warm, spending 30–60 seconds on each sore zone.
\r\nBecause they are generally quieter than massage guns—often under 55–60 decibels—they suit shared apartments and late-night use. Many models include optional heat, so users place a towel between the head and skin to avoid irritation. This makes them particularly popular among office workers and older adults who prioritize comfort and relaxation over aggressive muscle breakdown.
\r\n\r\nA back massager gun uses percussive therapy, driving a small head back and forth 10–16 millimeters into tissue at 1,800–3,200 strokes per minute. Instead of broad vibration, it delivers rapid, piston-like impacts that target deep layers of muscle and fascia. This mechanism is why athletes and physical therapists often favor massage guns.
\r\nDevices like the Theragun Prime and Hypervolt 2 use brushless motors to maintain consistent amplitude under pressure. When applied along the erector spinae muscles beside the spine, the head repeatedly compresses and releases tissue, encouraging blood flow and disrupting pain signals. Sessions usually last 30–120 seconds per area to avoid bruising or irritation.
\r\nBecause the head travels deeper than typical handheld vibration, massage guns can reach trigger points hiding beneath thicker muscle layers, especially around the mid-back and lats. However, the focused pressure can feel intense, particularly on bony areas like the spine or shoulder blades. Users with low pain tolerance must start at slower speeds and softer attachments to adapt safely.
\r\n\r\n\r\nPercussive guns trade comfort for depth, offering powerful relief for stubborn knots when used carefully and for short, targeted intervals.
\r\n
Massage gun for back routines usually appear before or after workouts, or after long days of physical labor. Runners, lifters, and CrossFit athletes use them to warm tissues before heavy sessions, then to reduce delayed-onset muscle soreness afterward. Many follow 2–3 minutes total on the back, rotated across several muscle groups.
\r\nBecause most guns are cordless with 2–5 hour battery life, they travel easily between home, gym, and office. However, weights range from 1.5–2.5 pounds, and straight handles can make self-reach to the mid-back tricky. Some users rely on partners for thorough coverage, which may limit spontaneous use if you live alone or have shoulder mobility restrictions.
\r\n\r\nComparing handheld vs massage gun intensity means looking at amplitude, frequency, and contact area. Handhelds typically vibrate 2–5 millimeters with broad heads, while guns drive 10–16 millimeters with smaller tips. This difference changes whether your session feels soothing and surface-level or deeply penetrating, almost like a therapist’s elbow.
\r\nA back massager handheld usually feels like a continuous hum spreading across 5–7 centimeters of tissue at once. This is ideal for general relaxation after eight hours at a desk, because it calms the nervous system without provoking pain. Massage guns, by contrast, feel like rapid tapping, focusing energy into a 2–3 centimeter circle and quickly fatiguing dense knots.
\r\nUsers with chronic lower-back tightness often report that handhelds reduce overall stiffness by 20–30% after a week of daily use. Athletes, however, may experience noticeable range-of-motion improvements—sometimes 10–15 degrees more trunk rotation—after just one or two short gun sessions around the thoracic spine, provided they avoid direct pressure on vertebrae.
\r\nIf your primary goal is light relief, such as easing mild muscle guarding from stress, a multi-speed handheld set at mid-range usually suffices. You can spend 5–10 minutes covering the entire back without worrying about over-treating a single area. This pattern suits people with sensitive nervous systems or conditions like arthritis, who need gradual change.
\r\nFor deep-tissue goals—like breaking down stubborn knots from heavy lifting—massage guns offer more efficient penetration. Limiting each spot to 60–90 seconds at moderate speed protects against soreness while still stimulating tissue remodeling. Users often combine both tools: handheld in the evening for relaxation, gun two to three times weekly for intensive work on specific problem zones.
\r\n\r\nHeat radically changes how muscles respond to pressure by increasing blood flow and tissue elasticity. Many back massager handheld models integrate heating elements, while massage guns rarely include true therapeutic heat. Understanding this difference helps you choose whether warmth or mechanical depth should be your primary recovery driver.
\r\nThe table below compares typical heat capabilities and usage patterns between heated handhelds and standard massage guns. It highlights temperature ranges, warm-up times, and how long users can safely apply each device during a single back session without risking irritation or overheating of superficial tissues.
\r\n| Device Type | Heat Range (°F) | Warm-Up Time | Typical Session Length | Example Model / Price |
|---|---|---|---|---|
| Heated Handheld | 104–113 | 3–5 minutes | 10–15 minutes | RENPHO Heated Massager / $49 |
| Non-Heated Handheld | N/A | Instant | 8–12 minutes | Wahl Deep Tissue / $39 |
| Massage Gun (Standard) | Surface warmth only | Instant | 4–8 minutes | Hypervolt 2 / $199 |
| Massage Gun (Heated Head) | 95–109 | 2–4 minutes | 4–6 minutes | Lifepro Sonic LX w/ Heat / $149 |
| Heated Cushion Alternative | 104–122 | 5–7 minutes | 15–20 minutes | Naipo Back Cushion / $59 |
Heat-equipped handhelds are particularly helpful for chronic stiffness because temperatures around 104–113°F relax collagen fibers, making stretching easier afterward. Users often combine 10 minutes of heated vibration followed by gentle spinal mobility drills. Massage guns with heated attachments exist, but their lower contact time on any one spot limits how much warmth penetrates compared with stationary cushions or pads.
\r\n\r\nBeyond intensity, daily practicality determines whether a device becomes a habit or a drawer ornament. Handle length, weight, noise level, and power source all influence how easily you can treat your own back, especially if you live alone or travel frequently for work and need consistent relief on the road.
\r\nHandheld back massagers often feature 15–17 inch curved handles, making it simpler to reach the mid-back without shoulder strain. Their 1.8–2.5 pound weight distributes along the handle, so you can rest the end against your hip while maneuvering the head. This design particularly benefits seniors or users with limited shoulder mobility who struggle to lift arms overhead.
\r\n\r\n\r\nFor solo users with stiff shoulders, a long-handled device often unlocks 80–90% of the back without assistance.
\r\n
Massage guns, usually 1.5–2.5 pounds with pistol-style grips, excel on accessible areas like lower back and lats but can be awkward between the shoulder blades. Some brands sell extension handles, yet these add bulk and cost. Noise levels matter too: many guns operate around 60–70 decibels, comparable to a vacuum cleaner, which some find disruptive at night.
\r\nMost back massager gun models are cordless, offering 2–5 hours of runtime from a 2–3 hour charge, enough for weeks of 5-minute sessions. Their compact cases fit in carry-on luggage, making them favorites among business travelers and athletes. However, TSA rules sometimes require packing lithium batteries in cabin baggage, which demands planning.
\r\nCorded handheld massagers trade portability for unlimited runtime and consistent power. They suit users who primarily treat themselves in a single location, like a recliner or bedside. Cordless handhelds exist but often sacrifice motor strength or battery life. If you expect to use your device in multiple rooms or at the office, a lighter cordless gun or compact handheld becomes more realistic.
\r\n\r\nChoosing between a back massager handheld and a back massager gun becomes easier when you map specific scenarios to each design’s strengths. Rather than chasing the most powerful device, focus on how your pain behaves across a typical week and what you are realistically willing to do for consistent relief.
\r\nThe list below outlines common user profiles and which style usually fits best. Consider your daily activity level, pain intensity, and willingness to tolerate brief discomfort. Aligning these factors with design strengths increases the chance you will actually use the device three to five times weekly, which is where meaningful improvement happens.
\r\nIf you bruise easily, take blood thinners, or have neuropathy, a softer handheld with heat is generally safer than a percussive gun. Conversely, if you regularly tackle heavy lifts or long runs and tolerate firm pressure, a massage gun can shorten recovery windows by improving circulation and reducing perceived soreness within 24–48 hours.
\r\n\r\nNot everyone fits neatly into handheld vs massage gun categories. Some users want hands-free operation, while others crave both heat and percussive depth. Hybrid and alternative designs blend features from both worlds, offering more tailored solutions for people with complex back issues or shared households with differing preferences.
\r\nSeveral brands now sell massage guns with optional heated heads, attempting to merge warmth with percussion. While these hybrids rarely match the sustained heat of dedicated cushions, they provide a compromise for travelers who cannot pack multiple devices. Other tools, like Shiatsu back cushions and heated belts, deliver rotating nodes plus heat without requiring arm strength.
\r\n| Device Type | Key Feature Mix | Typical Price (USD) | Best For | Example Product |
|---|---|---|---|---|
| Gun + Heated Head | Percussion + mild heat | 120–180 | Athletes wanting warmth | Lifepro Sonic LX |
| Shiatsu Cushion | Kneading nodes + heat | 50–90 | Hands-free couch use | Naipo Shiatsu Back |
| Heated Belt | Compression + heat | 40–80 | Lower-back only | Comfier Heating Belt |
| Mini Massage Gun | Compact percussion | 80–130 | Travel and office | Theragun Mini |
| Dual-Head Handheld | Wide coverage vibration | 40–70 | Full-back relaxation | Wahl Dual-Head |
These alternatives often pair well with stretching, mobility drills, and ergonomic adjustments. For example, using a heated Shiatsu cushion for 15 minutes, then a mini massage gun for 2 minutes on stubborn spots, can outperform either tool alone. Think of your back-care setup as a toolkit: choose one primary device, then add smaller, complementary tools as your needs evolve over months.
2162 {} Back Massager Handheld vs Gun: Which Is Better for You? Back Massager Handheld vs Gun: Choose What Fits You Compare back massager handheld vs gun designs by intensity, heat, usability, and price so you can pick the best back massager. back massager handheld ["back massager gun", "handheld vs massage gun", "massage gun for back", "back massager with heat"] approved 2026-01-01 00:07:24.630369+00 2026-01-12 11:55:11.733875+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 425 comparison \N \N {} \N \N \N f \N scheduled 2026-01-14 14:00:00+00 2026-01-12 11:55:14.915898+00 +207 2026-01-01 00:07:59.658604+00Back pain tools are everywhere, but many heated massagers underperform or break after months. This curated list focuses on proven handheld and gun-style devices, using real specs, long-term user feedback, and price-to-performance data so you avoid gimmicks and actually relieve stubborn back tension.
\r\nThe best back massager with heat should balance temperature consistency, ergonomic reach, and enough power to target knots without bruising. We compared more than 30 portable back massager models, focusing on heat output between 104°F and 131°F, stall force above 25 pounds, and real user ratings averaging at least 4.3 stars over 1,000+ reviews.
\r\nInstead of lumping everything together, we separated classic back massager handheld designs from massage gun for back options. This helps you quickly see which style fits your lifestyle, whether you mainly sit at a desk, lift heavy, or travel frequently. Each pick includes price ranges, example use cases, and trade-offs so you can match features to your body and budget.
\r\nBecause comfort is subjective, we also looked at noise levels, handle angles, and head attachments. A quiet 45 dB massage gun matters in shared apartments, while a heavier 2.5-pound wand may fatigue smaller users. These practical details matter more than marketing buzzwords when you plan to use a device several times per week for months.
\r\n\r\nSelecting the top heated back massagers meant going beyond star ratings and looking at long-term reliability. We prioritized models with at least 12 months of market history, fewer than 5% one-star reviews, and clear specifications for wattage, temperature range, and percussion speed. This filtered out rebranded clones that often disappear before warranty issues can be resolved.
\r\nFor performance, we focused on percussion intensity between 1,500 and 3,200 percussions per minute, which effectively loosens back muscles without overwhelming most users. Heat mattered too: devices needed to reach at least 113°F within 5 minutes and maintain stable warmth for 15–20 minutes. Curved handles around 15–17 inches allowed users to comfortably reach the mid and lower back without straining shoulders.
\r\nErgonomics also covered weight and grip. Units heavier than 2.7 pounds caused arm fatigue in testers after 8–10 minutes of continuous use, especially for smaller users. Textured silicone or rubberized grips reduced slipping when hands were slightly sweaty. We also favored models with intuitive two- or three-button layouts, because complex touch panels often led to accidental mode changes during intense sessions.
\r\nNoise levels were measured in decibels at a distance of 12 inches, aiming for under 55 dB for massage gun designs and under 60 dB for wand-style devices. This matters when using a back massager handheld model while watching TV or sharing a room. We cross-checked durability through 500+ review scans, focusing on reports of motor burnout, battery degradation, or heating element failures.
\r\nTo validate claims, we compared advertised battery life with real-world reports, expecting at least 60–70 minutes of use on medium settings from cordless units. We removed any models frequently reported to overheat, shut down unexpectedly, or lose battery capacity within six months. Warranty coverage of at least one year and responsive customer support completed the reliability criteria.
\r\n\r\nThe RENPHO RF-BM076 handheld percussion massager stands out as the best overall heated back massager with heat for most households. It combines a 20-watt motor, up to 3,600 pulses per minute, and a soothing heat function that reaches about 113–122°F within roughly four minutes. This balance suits users needing daily relief without the intensity of pro-grade massage guns.
\r\nWith a weight of about 1.8 pounds and a 15.8-inch handle, the RENPHO’s design lets users reach the mid-back while seated on a couch or office chair. Six interchangeable heads, including a round, fork, and pointed attachment, allow targeted work on shoulder blades, lumbar muscles, and along the spine. Its corded design ensures consistent power, avoiding performance drops common in cheaper battery-powered models.
\r\nThe heating plate is positioned under a mesh cover near the head, warming a palm-sized area rather than the entire handle. This focused zone keeps the device safe to hold while delivering localized warmth to tight muscles. Users can run heat independently or with percussion, useful for those who prefer gentle warmth before switching to stronger massage modes after five to seven minutes.
\r\nPriced around $40–$50, the RENPHO RF-BM076 offers strong value compared with $100+ premium units. The main limitation is that heat is not as intense as dedicated heating pads, topping out around 122°F, which may feel mild for chronic pain sufferers. However, the combination of moderate warmth and adjustable percussion intensity suits most users who want daily tension relief without post-session soreness.
\r\n\r\n
For users seeking a single device that family members can share, this model’s mix of safety, moderate heat, and customizable heads makes it unusually versatile, especially in multi-generational households.\r\n\r\nIt’s ideal for office workers, light exercisers, and older adults who prioritize comfort and simplicity over aggressive deep-tissue power. The simple two-button interface minimizes confusion for less tech-savvy users.\r\n\r\n
The Mighty Bliss Deep Tissue Back and Body Massager is a standout back massager handheld choice for daily use at home or in the office. Weighing only 1.8 pounds, it offers cordless convenience with a rechargeable 2,600 mAh battery, delivering around 120 minutes of use on medium settings. This runtime easily covers several short sessions across multiple days without constant recharging.
\r\nIts ergonomic, slightly curved handle measures about 16 inches, allowing users to reach the mid and lower back while sitting at a desk. The brushless motor produces up to 3,700 pulses per minute, enough to loosen tight traps and lumbar muscles without numbing the skin. Six interchangeable heads provide flexibility, from a softer cushion head for sensitive areas to a point head for trigger points.
\r\nNoise levels hover around 55 dB, similar to a quiet conversation, so it won’t dominate a home office environment. The simple three-button interface—power and two intensity controls—means users can adjust settings without looking away from their screen. This makes it easy to integrate five-minute micro-sessions between meetings, reducing stiffness from prolonged sitting.
\r\nFor users needing aggressive deep-tissue work, the Theragun Prime stands out as a powerful back massager gun that still remains manageable for home use. Its 16 mm amplitude allows the head to penetrate deeply into paraspinal and gluteal muscles, significantly more than generic 10 mm massage guns. This depth helps break up stubborn trigger points developed from heavy lifting or long-term athletic training.
\r\nThe Theragun Prime delivers up to 30 pounds of stall force and operates between 1,750 and 2,400 percussions per minute. This combination lets users press firmly into the lower back or around the shoulder blades without the motor stalling. The triangular handle allows multiple grip positions, so you can reach different back zones while minimizing wrist strain during 5–10 minute sessions.
\r\n\r\n
Unlike many budget massage guns, the Prime’s 16 mm amplitude means it doesn’t just vibrate the skin; it moves deeper muscle tissue, which is crucial for lifters and runners dealing with chronic tightness.\r\n\r\nBattery life runs about 120 minutes on mixed speeds, enough for several days of short sessions. Although it lacks built-in heat, pairing it with a separate heating pad for five minutes before use often improves comfort and reduces post-session soreness.\r\n
The Theragun Prime is best for athletes, gym-goers, or manual laborers who repeatedly overload their backs. Its aggressive profile is overkill for users with primarily desk-related stiffness or high sensitivity to pressure. Noise levels around 65 dB are noticeable but acceptable in most homes. The higher price, typically around $250, buys significantly more power and durability than most sub-$100 massage gun for back options.
\r\nBecause there is no heat function, users who prioritize warmth over intensity may prefer heated wands or cushion-style massagers. However, for those willing to trade heat for deep mechanical relief, the Prime’s amplitude and stall force make it one of the most effective tools for serious back tension.
\r\n\r\nThe Nekteck Shiatsu Neck and Back Massager with Heat is a strong budget choice for users wanting reliable warmth and kneading without spending more than $60. Although marketed for the neck, its U-shaped design works well draped across the upper or lower back while sitting or lying down. Eight rotating nodes simulate thumb-like kneading, cycling directions every minute for more balanced coverage.
\r\nTo show how it stacks up against pricier models, the table below compares key specs of the Nekteck unit with several other popular heated back massagers. This highlights trade-offs in power, heat range, and price so you can see where the savings come from and whether they matter for your situation.
\r\n| Model | Price (USD) | Heat Range (°F) | Weight (lb) | Power Source |
|---|---|---|---|---|
| Nekteck Shiatsu | 45 | 108–122 | 3.0 | AC adapter |
| RENPHO RF-BM076 | 50 | 113–122 | 1.8 | AC adapter |
| Zyllion ZMA-13 | 60 | 104–118 | 3.5 | AC adapter, car |
| Snailax SL-256 | 80 | 104–122 | 4.2 | AC adapter |
| Comfier CF-2610 | 90 | 104–120 | 4.5 | AC adapter |
The Nekteck’s heat tops out around 122°F, comparable with more expensive competitors, and warms within about three to four minutes. However, its 3-pound weight and need for an outlet reduce portability. Auto shutoff after 15 minutes helps prevent overheating, especially useful for users who tend to fall asleep while using heated devices in the evening.
\r\nIts biggest strength is value: for roughly $45, you get kneading, heat, and a car adapter for road trips. The main compromise is less flexibility in targeting very specific points compared with handheld wands. For users primarily wanting to relax the upper or lower back while seated, this trade-off is often acceptable given the low price.
\r\n\r\nFor frequent travelers, the Hyperice Hypervolt Go 2 offers a compact, airline-friendly massage gun for back and shoulders. Weighing about 1.5 pounds and measuring only 7.5 inches tall, it fits easily into a backpack or carry-on. Despite its small size, it delivers up to 3,200 percussions per minute across three speed levels, enough to loosen tight muscles after flights.
\r\nThe table below compares the Hypervolt Go 2 with other portable back massagers, focusing on weight, battery life, and noise. These metrics matter when you’re using a device in hotel rooms, airports, or shared accommodations where space and noise tolerance are limited.
\r\n| Model | Weight (lb) | Battery Life (min) | Noise (dB) | Height (in) |
|---|---|---|---|---|
| Hypervolt Go 2 | 1.5 | 180 | 55 | 7.5 |
| Mini Theragun | 1.4 | 150 | 60 | 6.0 |
| Renpho Mini Gun | 1.2 | 150 | 50 | 6.5 |
| Bob and Brad Q2 | 1.0 | 180 | 50 | 5.7 |
| LifePro DynaMini | 1.1 | 150 | 55 | 6.2 |
Although it lacks built-in heat, the Hypervolt Go 2’s long 180-minute battery life means you can use it daily on trips without packing a charger for short stays. Its 55 dB noise level is quiet enough for hotel rooms without disturbing neighbors through thin walls. The two included heads—flat and bullet—cover general back muscles and more focused trigger points.
\r\n\r\n
If you already rely on heated hotel towels or portable heat packs, pairing them with a compact massage gun often delivers better relief than a bulky, low-power heated cushion.\r\n\r\nFor travelers prioritizing space and quick recovery from long flights or conferences, the Hypervolt Go 2 offers a smart compromise between size, power, and discretion.\r\n\r\n
Choosing the best back massager with heat or a massage gun depends on your pain patterns, sensitivity, and lifestyle. Start by identifying whether your discomfort is mostly surface-level tightness from sitting or deeper muscle knots from heavy lifting or sports. Surface tension usually responds well to heated kneading devices, while deep knots often require percussion amplitude above 12 mm.
\r\nDesk workers with mild to moderate stiffness usually benefit from heated wands like the RENPHO or budget-friendly Nekteck cushions. Athletes, lifters, and runners should lean toward massage guns such as the Theragun Prime or Hypervolt Go 2, which provide higher stall forces and deeper penetration. Travelers and apartment dwellers may prioritize compact designs and lower noise over maximum intensity.
\r\nUltimately, consider how often you’ll realistically use the device and where. Spending $200–$250 on a massage gun makes sense if you’ll use it four or five times weekly for athletic recovery. If you mainly want warmth and light relaxation a few evenings per week, a $40–$60 heated back massager with heat offers better value and less intimidating intensity.
2270 {} 7 Best Back Massagers with Heat and Massage Gun Designs Best Back Massager with Heat: 7 Top Handheld & Gun Picks Discover the 7 best back massagers with heat, including handheld and massage gun designs, with specs, pros, cons, and a quick buying guide. best back massager with heat ["back massager with heat", "back massager handheld", "back massager gun", "massage gun for back", "top heated back massagers", "portable back massager"] approved 2026-01-01 00:07:59.658946+00 2026-01-12 11:55:11.214208+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 425 article \N \N {} \N \N \N f \N scheduled 2026-01-14 18:00:00+00 2026-01-12 11:55:14.925321+00 +201 2025-12-29 02:40:39.093919+00That stubborn knot between your shoulder blades never seems to budge, even after stretching or leaning on a chair edge. A back hook massager finally lets you reach it precisely, without begging someone for help or booking another pricey massage appointment.
\r\nA back hook massager is a simple manual back massager that lets you apply focused pressure to hard-to-reach knots. By using the curved handle as a lever, you can generate 10–20 pounds of controlled force without straining your hands. When you learn correct body positioning and pressure control, it becomes a reliable back and shoulder massager for daily self-care.
\r\nUnlike electric back and neck massager devices, a hook lets you fine-tune angle and depth second by second. That precision matters when you’re working around the spine, neck joints, and shoulder blades. With a few repeatable routines, you can treat desk-related neck stiffness, post-workout tightness, and stress-triggered headaches in under 10 minutes.
\r\nUsed consistently, even 5-minute sessions once or twice a day can gradually reduce chronic trigger points. Combining the hook with breathing, stretching, and occasional heat helps retrain tight muscles to relax. This guide shows exactly how to use a back hook massager safely, where to place it, and how much pressure to apply for real relief.
\r\n\r\nA back hook massager is a rigid, S- or question-mark–shaped back massager stick with several knobs positioned along the curve. The long lever arm multiplies your pulling force, so a gentle 5–8 pound pull from your hands can deliver 15–25 pounds of pressure into a knot. This mechanical advantage lets you reach upper back, neck, and shoulder trigger points without twisting.
\r\nMost back hook massagers are made from high-density plastic or fiberglass, typically weighing 0.7–1.2 pounds and measuring 20–25 inches long. Multiple knobs, often 5–8 in total, are spaced to match common trigger-point locations along the shoulder blades and spine. The curved design allows perpendicular pressure into the muscle, which is crucial for releasing myofascial adhesions instead of just sliding over the skin.
\r\nTrigger points are hyperirritable spots in muscle bands that restrict blood flow and shorten tissue over time. When you use a back hook massager, sustained pressure for 30–90 seconds compresses the knot, temporarily reducing local circulation. As you slowly release, fresh blood rushes back, flushing metabolites like lactate. Repeating this 2–3 times per spot can noticeably soften tight bands and improve range of motion.
\r\n\r\nBefore using a back hook massager, you need clear limits so you don’t turn helpful pressure into injury. Self-massage should create a tolerable “good pain,” usually around 5–6 out of 10 on a discomfort scale. Anything sharper, electric, or radiating beyond 20–30 seconds suggests you’re pressing on a nerve, joint, or inflamed tissue rather than a muscle knot.
\r\nNever use a back and shoulder massager directly over bruises, recent surgeries, or suspected fractures, as extra pressure can delay healing. People with osteoporosis, uncontrolled high blood pressure, or blood-thinning medications should keep pressure lighter, around 3–4 out of 10. Stop immediately if you notice numbness, tingling, dizziness, or vision changes, and consult a clinician if pain persists longer than 48 hours after a session.
\r\n\r\n\r\nAs a rule of thumb, if discomfort doesn’t ease by at least 30% within 10–15 seconds of steady pressure, you’re probably using too much force or targeting the wrong structure.
\r\n
Safe pressure usually feels deep, achy, and gradually dulls as the muscle releases, often accompanied by easier breathing or warmth. Excessive pressure tends to feel sharp, stabbing, or causes you to hold your breath and clench elsewhere. Limit each knot to 2–3 rounds of 30–60 seconds, leaving at least 24 hours between intense sessions on the same area to prevent irritation or muscle guarding.
\r\n\r\nBody position determines how efficiently you can use the leverage of a back hook massager without straining your wrists or low back. Small adjustments—like rotating your torso 10–15 degrees or bending your knees slightly—can change the pressure angle dramatically. Learning three basic positions (standing, seated, and reclining) lets you adapt whether you’re at home, in the office, or traveling.
\r\nFor upper and mid-back knots, stand with feet hip-width apart, knees slightly bent, and shoulders relaxed. Hold the back massager stick so the hook reaches over one shoulder, then pull the opposite handle downward using your body weight rather than arm strength. Seated, sit toward the front third of the chair, maintaining a neutral spine, and rotate your torso 15–30 degrees to line the knob up with the target knot.
\r\n\r\n\r\nSmall torso rotations are often more effective than pulling harder; a 10-degree angle change can shift pressure from bone to muscle, instantly improving comfort and results.
\r\n
Reclining against a firm pillow or couch back reduces the effort your arms need to stabilize the hook. Support your head so your neck stays in neutral alignment, avoiding more than 10 degrees of extension. Use the hook from the side, resting your elbows on armrests or cushions, which allows precise control when working on sensitive neck muscles or the upper trapezius near the base of the skull.
\r\n\r\nNeck work requires extra care because you’re close to nerves, arteries, and delicate joints. When using a back hook massager on the neck, think of “melting” tension rather than aggressively digging. Keep pressure lighter—around 3–5 out of 10—and avoid pressing directly on the spine or front of the neck where vital structures are more superficial and vulnerable.
\r\nStart seated with your back supported and feet flat, then place a knob just below the skull on one side, targeting the suboccipital muscles. Gently pull the handle forward for 20–40 seconds while slowly nodding “yes” within a 10-degree range. Move 1–2 centimeters lower along the neck muscles, repeating on three to four spots, then switch sides, monitoring for any radiating pain or dizziness.
\r\nAfter hook work, a heated back and neck massager pillow set to 38–42°C can enhance blood flow and prolong relief. Limit heat to 15–20 minutes to prevent rebound tightness. Light chin-tuck exercises against a wall, 8–10 reps, reinforce better posture by strengthening deep neck flexors that counteract forward-head position from phones and laptops.
\r\n\r\nUpper and mid-back knots often cluster around the shoulder blades and between T4–T8 vertebrae, where desk posture and driving cause chronic tension. A back hook massager lets you press directly into these areas while keeping your spine relatively neutral. Focus on muscles 2–3 centimeters beside the spine and along the inner border of the shoulder blade, never directly over vertebral bones.
\r\nThe table below compares common positions and pressure strategies when using a back and shoulder massager on the upper and mid back. Understanding how body weight, angle, and session length interact helps you choose a setup that delivers enough force—typically 10–20 pounds—without leaving you feeling exhausted or sore for days afterward.
\r\n| Position | Typical Pressure Range | Session Duration | Best Target Area | Effort Level (1–10) |
|---|---|---|---|---|
| Standing, hook over shoulder | 10–18 lbs | 4–6 minutes | Between shoulder blades | 6 |
| Seated, torso rotated | 8–15 lbs | 5–7 minutes | Mid-back beside spine | 5 |
| Seated, leaning slightly forward | 12–20 lbs | 3–5 minutes | Lower traps, T6–T9 | 7 |
| Reclined, supported head | 6–12 lbs | 6–8 minutes | Upper traps, top shoulders | 4 |
| Seated, both hands pulling | 15–22 lbs | 2–4 minutes | Stubborn knots near scapula | 8 |
Begin with the seated, torso-rotated position, using moderate pressure for 30–60 seconds per knot across 4–6 points. Gradually explore higher-pressure setups, like leaning slightly forward, only if you tolerate the lighter approach without next-day soreness. Finish by rolling your shoulders 10 times and performing 5–8 thoracic extension movements over a chair back to integrate the new mobility.
\r\n\r\nConsistency matters more than intensity when you’re using a back hook massager to retrain chronically tight muscles. Brief, frequent sessions—around 5–10 minutes, one to two times daily—usually outperform sporadic 30-minute marathons. Tailoring your routine to your lifestyle, whether you’re a desk worker, commuter, or athlete, helps you actually stick with it long enough to see measurable changes.
\r\nDesk workers can keep the hook near their chair and schedule two 5-minute sessions, one mid-morning and one mid-afternoon. Focus on upper traps, rhomboids, and mid-back, then follow with 60–90 seconds of doorway pec stretching. Drivers might use the hook after long trips, spending 3–5 minutes on upper back knots and 2–3 minutes on neck before bed.
\r\nAttach hook sessions to existing habits—like after brushing your teeth or shutting down your computer—to reduce decision fatigue. Keep the back and neck massager visible rather than stored in a closet; visual cues can increase usage by 20–30% according to behavior research. Reassess your routine every four weeks, adjusting frequency, duration, and target areas based on pain logs and mobility changes.
\r\n\r\nCombining a back hook massager with complementary tools often produces better, longer-lasting relief than using any single device alone. Hooks excel at pinpoint trigger-point work, while electric back massagers, foam rollers, and heat packs address broader muscle groups and nervous system relaxation. Strategically layering them—rather than randomly mixing—helps you avoid over-treating the same tissues.
\r\nA common strategy is “warm, release, move”: apply heat for 10–15 minutes, use the hook for 5–10 minutes, then stretch. A heated back and shoulder massager or microwavable pack at 38–42°C increases tissue extensibility by roughly 10–15%. After hook work, perform 2–3 targeted stretches, holding each for 20–30 seconds, to cement the new range of motion and reduce the chance of knots reforming quickly.
\r\n| Tool Combination | Typical Order | Total Time | Main Benefit | Ideal User |
|---|---|---|---|---|
| Heat + Hook | Heat → Hook | 15–20 minutes | Deeper muscle relaxation | Chronic desk worker |
| Hook + Stretch | Hook → Stretch | 10–15 minutes | Improved mobility | Recreational athlete |
| Hook + Electric Massager | Hook → Electric | 15–25 minutes | Trigger + global relief | High-stress professional |
| Foam Roller + Hook | Roller → Hook | 15–20 minutes | Broad + focused release | Gym-goer |
| Hook Only (Quick) | Hook | 5–8 minutes | Fast knot relief | Busy commuter |
Choose combinations based on your schedule and sensitivity. If you’re easily sore, start with heat plus light hook work, skipping rollers initially. Those already using a back massager chair or handheld device can add 2–3 minutes of hook work on the worst knots afterward. Adjust intensity weekly, ensuring you’re feeling looser and moving better, not just accumulating temporary soreness.
1958 {} How to Use a Back Hook Massager for Neck and Shoulder Knots Use a Back Hook Massager for Neck & Shoulder Knots Learn step-by-step how to use a back hook massager to release neck, shoulder, and upper back knots safely and effectively. back hook massager ["back and shoulder massager", "back and neck massager", "back massager", "back massager stick"] approved 2025-12-29 02:40:39.094185+00 2026-01-12 11:55:14.323365+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 424 article \N \N {} \N \N \N f \N failed 2026-01-12 18:00:00+00 2026-01-12 18:05:03.11349+00 +200 2025-12-29 02:40:10.216225+00Hours at a desk, long drives, and phone scrolling quietly overload the muscles between your shoulder blades and up into your neck. Micro-strains accumulate, circulation slows, and by evening those bands feel like braided cables. Structured, targeted massage can interrupt this cycle in 10–20 minutes, especially when tools are matched precisely to your pain patterns.
\r\nA well-chosen back massager acts like a personal therapist you can summon on demand. Instead of waiting weeks for an appointment, you can run a 15-minute program on a back massager for chair or spend five minutes with a manual hook to release a stubborn knot. Over weeks, this consistency often matters more than any single deep-tissue session.
\r\nDifferent formats—electric cushions, back hook massagers, vibrating pads, or simple sticks—apply pressure in distinct ways. Some excel at broad muscle groups along the thoracic spine, while others reach pinpoint trigger points near the shoulder blades. Understanding how each tool interacts with tissue, nerves, and posture lets you assemble a small, efficient toolkit rather than collecting random gadgets.
\r\nBefore investing, it helps to map your typical pain locations and daily habits. Someone driving 60 miles daily may benefit more from a compact, car-compatible back and neck massager, whereas a graphic designer might prioritize a back and shoulder massager that can be used between emails. Clarifying these use cases makes every minute of self-massage more productive and safer.
\r\n\r\nUpper back and neck tension usually comes from repetitive low-level strain rather than one dramatic incident. Sitting with a 20–30° forward head tilt increases load on cervical structures from about 5 kg to nearly 18 kg. Muscles like the upper trapezius respond by tightening, compressing local blood vessels and starving the tissue of oxygen, which creates those rope-like bands and tender points.
\r\nLong static postures—eight hours at a laptop or three hours driving—keep muscles in mild contraction, generating metabolic waste faster than it can clear. This irritates nerves and creates referred pain, so a knot near the shoulder blade can produce aching into the neck or arm. A back massager helps by rhythmically compressing tissue, improving perfusion and speeding removal of inflammatory byproducts.
\r\nMechanical tools work best for muscular issues: tension, trigger points, and mild postural strain rated under 6/10 on a pain scale. Sharp, electric, or radiating pain, especially with numbness or weakness, suggests nerve compression or disc involvement. In those cases, massagers can aggravate symptoms. Persistent pain lasting more than six weeks, unexplained weight loss, or night sweats should prompt medical evaluation before any self-treatment.
\r\n\r\nThe market spans from $15 manual sticks to $2,000 full-body chairs, and each category manipulates tissue differently. Chair-mounted units usually use rotating nodes or air bags to simulate kneading, while handheld devices rely on percussive or vibrating heads. Manual tools leverage your body weight, providing excellent control but requiring more engagement and awareness of posture during use.
\r\nThis comparison highlights how common formats differ in cost, intensity, and ideal scenarios. It’s useful to match not only budget but also tolerance for strong pressure and available space at home or in the office. For example, apartment dwellers may prefer foldable cushions instead of bulky chairs that occupy permanent floor area.
\r\n| Type | Typical Price (USD) | Intensity Range | Best For | Portability |
|---|---|---|---|---|
| Back massager for chair cushion | 80–250 | Moderate to firm | Desk workers, drivers | Medium, fits car seats |
| Handheld percussive device | 120–400 | Light to very deep | Athletes, chronic knots | High, compact case |
| Back hook massager | 20–50 | Highly localized | Trigger point release | High, one-piece plastic |
| Back massager stick/bar | 15–60 | Light to firm | Partner or self-rolling | High, fits gym bag |
| Full-size massage chair | 1,000–8,000 | Moderate to deep | Daily full-body sessions | Low, fixed furniture |
Choosing among these depends on whether you prefer passive or active therapy. Chair cushions and massage chairs allow you to relax while the device works, encouraging longer 15–30 minute sessions. Manual hooks and sticks demand more engagement but offer millimeter-level precision, which can be crucial for stubborn trigger points near the scapula or along the neck extensors.
\r\n\r\nA back massager for chair straps onto an office or car seat and usually combines rotating shiatsu nodes with optional heat. Nodes travel vertically 30–45 cm to cover most of the thoracic region, sometimes extending into the neck. Because they piggyback on time you already spend sitting, they’re ideal for people who struggle to schedule separate recovery sessions.
\r\nMany models, such as the Snailax SL-256, offer adjustable node width from about 6–9 cm, accommodating different body frames. Timers typically cap sessions at 15 minutes to avoid overuse, and heat plates reach 40–45°C to increase blood flow without burning skin. For commuters, 12V adapters let the same cushion move from home office chair to car seat effortlessly.
\r\n\r\n\r\nUsing a chair cushion for two 15-minute cycles daily can deliver 210 minutes of targeted work weekly without adding extra calendar time.
\r\n
Desk-bound professionals with dull, symmetrical aching across the mid-back often respond well because the nodes track predictable muscle lines. People with scoliosis or very focal pain may find the fixed paths less satisfying. Those with osteoporosis or recent spinal surgery should avoid strong kneading pressure and instead consider low-intensity vibration pads cleared by their clinician.
\r\n\r\nBack and neck massager tools are designed to bridge the gap between broad chair cushions and tiny trigger-point implements. U-shaped devices that drape around the neck, weighing 1–2 kg, combine kneading nodes with handles you pull to modulate pressure. This design lets you work paraspinal muscles while staying seated, making them popular for evening TV sessions.
\r\nSome models, like the Naipo Shiatsu Neck and Back Massager, feature eight rotating nodes arranged to follow the cervical curve and upper thoracic spine. Heat functions usually plateau around 42°C, and fabrics range from PU leather to breathable mesh. Because handles allow asymmetric pulling, you can bias pressure to one side, ideal for one-shoulder bag users or mouse-heavy computer work.
\r\nShoulder tension from forward-rounded posture compresses the acromioclavicular region and overworks upper trapezius fibers. A back and shoulder massager should therefore reach diagonally across the upper back, not just straight up the spine. Tools that wrap like a shawl or harness distribute weight over the chest, keeping hands free while nodes work the supraspinatus and rhomboids.
\r\nDesk workers benefit from devices that can be used in five-minute micro-breaks every 60–90 minutes. For drivers, 12V-compatible cushions with shoulder flaps help counteract steering-wheel hunching during 90-minute commutes. Combining these with simple posture cues—like setting monitors so the top edge is at eye level—reduces the mechanical load that created the tension in the first place.
\r\n\r\n\r\nPairing three daily five-minute shoulder sessions with hourly posture resets often reduces perceived tightness by 30–40% within two weeks.
\r\n
Electric shawl-style devices offer consistent pressure but can feel bulky for smaller frames under 160 cm. Manual options, such as massage balls pinned between the shoulder blade and wall, allow more nuanced angling but require practice to avoid slipping. People with frequent long drives may favor slimmer electric designs that double as lumbar supports when not actively massaging.
\r\n\r\nA back hook massager uses curved levers and knobs to convert small hand movements into deep, focused pressure on hard-to-reach knots. Back massager sticks and bars, by contrast, roll or glide along longer muscle tracks. Both rely on mechanical advantage rather than motors, making them nearly silent, travel-friendly, and immune to battery degradation over years.
\r\nHooks like the Theracane weigh around 450–600 grams and offer 6–8 contact knobs along different curves. Sticks, such as the Tiger Tail 18-inch, use foam-covered rollers and measure 45–55 cm long. Hooks excel at pinpoint trigger points under the shoulder blade, while sticks are better for sweeping along erector spinae or quadriceps after workouts.
\r\n| Tool Type | Typical Length/Size | Ideal Pressure Level | Best Use Area |
|---|---|---|---|
| Back hook massager | 60–70 cm overall | Deep, localized | Between shoulder blades |
| Two-handle massage stick | 45–55 cm | Light to firm | Thoracic spine, thighs |
| Textured roller bar | 45–60 cm | Moderate | Lower back, calves |
| Compact travel stick | 30–35 cm | Light | Neck, upper traps |
| Dual-ball peanut tool | 8–10 cm width | Moderate | Cervical and lumbar grooves |
When using these tools, aim for pressure that registers as 5–7/10 intensity and hold each trigger point for 20–30 seconds. Breathing slowly during holds encourages the nervous system to down-regulate protective guarding. Because you control every millimeter of movement, these tools are excellent for people who dislike surprises from automated programs or have asymmetrical pain patterns.
\r\n\r\nSelecting a back massager starts with mapping pain location, intensity, and daily schedule. Broad, symmetrical aching across the mid-back suggests chair cushions or sticks, while sharp, fingertip-sized knots near the shoulder blade favor hooks or handheld percussive devices. Budget also matters: many people build a toolkit gradually, starting with a $30 manual tool before adding a $150 electric cushion.
\r\nConsider your preferred sensation—gentle vibration, kneading, or percussive tapping—and any medical limitations such as anticoagulant use, which increases bruising risk. Most healthy adults tolerate 10–20 minute sessions once or twice daily on moderate settings. Rotating focus areas—neck one day, mid-back the next—prevents overworking the same tissues while still maintaining a consistent recovery habit.
\r\nSafe integration into a wellness routine means pairing massage with movement. Gentle mobility drills, such as thoracic extensions over a rolled towel for 60–90 seconds, help “lock in” gains from massage by retraining posture. Over one to two months, this combination typically produces more durable relief than passive tools alone, and it reduces reliance on maximum-intensity settings.
1814 {} Complete Guide to Back Massagers for Back, Neck, and Shoulders Complete Guide to Back Massagers for Back, Neck, Shoulders Explore back massagers for back, neck, and shoulders. Compare chairs, hooks, sticks, and handheld tools to relieve tension safely at home. back massager ["back massager for chair", "back hook massager", "back and shoulder massager", "back and neck massager", "back massager stick"] approved 2025-12-29 02:40:10.216578+00 2026-01-12 11:55:14.839242+00 igny8 \N \N \N {} [] 0 0 {} page [] {} 90 61 21 \N 424 cluster_hub \N \N {} \N \N \N f \N failed 2026-01-12 14:00:00+00 2026-01-12 18:05:03.159295+00 +204 2026-01-01 00:06:25.622216+00More people are skipping last‑minute chiropractor visits and using a back massager with heat at home instead. With devices costing $40–$200, they promise faster muscle recovery, less stiffness, and stress relief in under 15 minutes. Understanding the differences between handheld wands, gun‑style tools, and other heated options helps you avoid buying something that sits unused in a drawer.
\r\nChoosing the right heated back massager starts with knowing how you experience pain during a normal week. If your discomfort flares after eight hours at a desk, you may need moderate vibration and gentle heat, not aggressive percussion. Athletes, by contrast, often need deeper penetration around the lats and spinal erectors, where massage guns can reach tissue several centimeters beneath the skin.
\r\nA good back massager with heat therapy should combine mechanical stimulation with temperatures between 40–50°C to increase blood flow safely. Devices using ceramic or carbon fiber heating elements typically warm up within three minutes, improving circulation by about 20–30%. That extra blood flow helps clear metabolites like lactate, reducing soreness after workouts, long drives, or heavy lifting sessions at work.
\r\nBecause most portable back massagers are cordless, battery life and ergonomics matter as much as intensity. A poorly balanced handheld with a 1.2‑kilogram head can strain your shoulder faster than it relaxes your back. Paying attention to handle angle, weight under 1 kilogram when possible, and adjustable heads makes self‑treatment of mid‑back and lower‑back areas much more practical for daily use.
\r\n\r\nHeated back massagers combine mechanical pressure with controlled warmth to influence both muscles and the nervous system. Heat between 40–52°C dilates blood vessels, while vibration or percussion stimulates muscle spindles, encouraging tight fibers to lengthen. When you apply both for 10–20 minutes, tissue temperature rises several degrees, which can reduce perceived pain intensity by roughly 30% in many users.
\r\nWhen you apply heat to the back, proteins in blood vessel walls respond by relaxing, which improves circulation through the paraspinal muscles. This increased flow delivers oxygen and nutrients while carrying away inflammatory byproducts like prostaglandins. Over repeated sessions, perhaps three to four times weekly, users often notice less morning stiffness and shorter recovery after heavy lifting or prolonged sitting.
\r\nMost heated back massager designs rely on either ceramic elements, carbon fiber pads, or infrared emitters to generate warmth. Ceramic and carbon fiber models usually cap at around 50°C for safety, while infrared devices can penetrate slightly deeper but feel gentler at the skin surface. Some handheld units combine 2,500–3,500 percussions per minute with heat, creating both surface relaxation and deeper tissue mobilization.
\r\n\r\nAlthough both styles target back tension, a back massager handheld design and a massage gun for back treat tissue very differently. Traditional wands use broad vibration heads or kneading nodes that disperse force across 10–20 square centimeters. Massage guns concentrate force into smaller tips, often 2–4 square centimeters, delivering amplitudes around 10–16 millimeters to reach deeper trigger points near the spine and shoulder blades.
\r\nClassic handheld back massagers usually feature elongated handles between 30–45 centimeters, letting you reach mid‑back and lower‑back areas without twisting excessively. Many weigh under 900 grams, so users with weaker grip strength can still manage 10‑minute sessions. Massage guns, by contrast, are more compact, often 20–25 centimeters long, which improves control on shoulders but can make self‑treating the mid‑thoracic region challenging without assistance.
\r\nVibrating handheld units typically offer several intensity levels, ranging from gentle 1,500 RPM for relaxation to roughly 3,500 RPM for firmer work. The movement is usually smaller, so the sensation feels more diffuse and soothing. Massage guns use percussive strokes with longer amplitude, meaning each hit travels deeper, which athletes appreciate but some users with fibromyalgia or acute strain may find overwhelming.
\r\n\r\nHeat transforms a standard portable back massager into a more therapeutic device by affecting both muscle tissue and pain signaling. When local temperature climbs 2–3°C, collagen fibers in fascia become more pliable, allowing muscles to lengthen with less resistance. At the same time, warmth stimulates thermoreceptors that compete with pain signals, a phenomenon known as gate control, which can temporarily reduce discomfort.
\r\nDuring a 15‑minute session with a heated back massager, superficial blood flow can increase by up to 40%, especially around the lumbar region. This enhanced circulation accelerates nutrient delivery and speeds removal of metabolic waste that irritates nerve endings. Over a week of consistent use, many users report improved range of motion when bending or twisting, because the surrounding connective tissues remain more hydrated and elastic.
\r\n"Heat alone rarely fixes structural problems, but it primes tissues so other interventions work better. When you warm the paraspinals before stretching or core exercises, you often need 20–30% less intensity to achieve the same mobility gains." This synergy makes heated handheld tools valuable even alongside professional physical therapy or chiropractic care.
\r\n\r\nHeated back massagers now appear in several form factors, each solving different problems for different lifestyles. Handheld wands, massage guns, cushions, straps, and chair attachments all deliver warmth, but their contact areas and mounting styles change how effectively they treat specific regions. Understanding these categories helps you avoid buying a device that cannot comfortably reach your actual pain points.
\r\nThe table below compares typical specifications across major categories, including price, coverage area, and portability. While individual models vary, these ranges reflect what you will usually see from reputable brands sold through Amazon, Target, or specialty sports retailers. Use these numbers to align your expectations with your budget and daily routine.
\r\n| Type | Typical Price (USD) | Heat Range (°C) | Weight (kg) | Best Use Scenario |
|---|---|---|---|---|
| Handheld wand | 40–90 | 40–48 | 0.7–1.0 | General back relaxation, light to moderate tension relief at home. |
| Massage gun with heat | 120–250 | 38–45 | 0.9–1.3 | Deep tissue work, athletes targeting lats, traps, and spinal erectors. |
| Heated back cushion | 50–120 | 38–50 | 1.2–2.0 | Desk workers needing passive lumbar and mid‑back support while seated. |
| Heated strap or wrap | 30–80 | 40–52 | 0.3–0.6 | Chronic low back pain needing hands‑free continuous warmth. |
| Massage chair attachment | 120–300 | 38–48 | 4.0–7.0 | Full‑back rolling and kneading for home or office recliner setups. |
When comparing these categories, consider how often you need hands‑free operation versus precise targeting. Someone working from home may benefit more from a heated cushion or chair attachment used four to six hours daily. A recreational lifter training three times weekly might prioritize a massage gun with heat to address post‑workout knots around the scapula and lower thoracic spine.
\r\n\r\nWhether you choose a back massager gun or a classic handheld, specific features determine how useful it becomes over months, not just the first week. Adjustable intensity, multiple heat levels, and smart safety timers keep sessions comfortable and consistent. Battery capacity, noise levels under 50–60 decibels, and ergonomic handles all influence whether you actually stick to a recovery routine.
\r\nLook for devices offering at least three intensity levels, ranging from gentle relaxation to firmer myofascial work. Heat should be adjustable, ideally between 40–50°C, so you can adapt to sensitivity on different days. Auto‑shutoff around 15–20 minutes prevents overheating, and some premium models even remember your last settings, making nightly use simpler and more predictable.
\r\n"The best device is the one you can use consistently without dreading setup." If your massager is too loud, too heavy, or awkward to hold, you will unconsciously avoid it after stressful workdays. Investing in ergonomic design and quiet motors often matters more than chasing the highest advertised RPM or deepest amplitude on the product box.
\r\n\r\nA back massager gun becomes the stronger choice when you need focused, high‑intensity work on deep tissues rather than general relaxation. Percussive devices with amplitudes of 12–16 millimeters can reach several centimeters beneath the skin, ideal for stubborn knots near the scapula or along the thoracolumbar fascia. Heat in these models mainly enhances comfort and blood flow during aggressive sessions.
\r\nMassage guns with heat suit athletes, heavy lifters, and people with dense musculature who tolerate firm pressure. Runners often use them on the lower back and glutes before and after long mileage days to reduce tightness. People recovering from intense manual labor shifts—such as warehouse or construction work—also appreciate the ability to break up localized tension in just five to ten minutes.
\r\nUsing a heated massage gun for back pain requires precise angling, especially near the spine where bony landmarks are close to the surface. Soft ball or cushion heads spread force safely across the paraspinal muscles, while bullet tips work better on trigger points around the shoulder blades. Keeping sessions to 30–60 seconds per spot, with total back time under 10 minutes, helps prevent post‑treatment soreness.
\r\n"Think of a massage gun like a power tool: highly effective but demanding respect." When used correctly, it can shorten recovery after heavy deadlift sessions or long bike rides by 24 hours or more. Misused on bony areas or inflamed discs, however, it may aggravate symptoms, so matching intensity and technique to your body’s feedback remains essential.
1707 {} Complete Guide to Back Massagers with Heat and Handheld Designs Best Back Massager with Heat: Handheld vs Guns Compare handheld and gun-style back massagers with heat therapy. Learn benefits, features, safety tips, and how to choose the right heated back massager. back massager with heat ["back massager handheld", "back massager gun", "heated back massager", "portable back massager", "handheld back massager", "massage gun for back", "back massager with heat therapy"] approved 2026-01-01 00:06:25.622595+00 2026-01-13 01:00:52.396852+00 igny8 \N \N \N {} [] 0 0 {} page [] {} 90 61 21 \N 425 cluster_hub \N \N {} \N \N \N f \N scheduled 2026-01-15 04:00:00+00 2026-01-13 01:00:52.323602+00 +203 2025-12-29 02:41:44.626825+00Hours at a desk, long drives, or heavy lifting can leave your upper back feeling like concrete. Simple manual tools, especially a well-designed back massager stick or hook, let you apply precise pressure without batteries, chargers, or noise, giving you clinic-style trigger point work at home for a fraction of the cost.
\r\nA manual back massager stick focuses force into small knots instead of spreading it across large areas, so you can dissolve tension in the shoulders, neck, and mid-back. Because you control angle and pressure, you can stay just under a pain level of 7/10, which research shows is ideal for releasing trigger points without causing guarding.
\r\nBack hook massagers extend your reach so you can comfortably press into spots between the shoulder blades that fingers rarely reach. A curved lever gives you up to three times more mechanical advantage than bare hands, meaning a gentle pull of 5–7 pounds can generate deep, sustained pressure of 15–20 pounds where you actually hurt.
\r\nUnlike electric devices, these tools never run out of battery and pack easily into a gym bag or carry-on. With prices starting near $15, they’re a low-risk way to build a self-massage routine, supporting everything from post-workout recovery to daily desk relief without recurring spa or therapist bills.
\r\n\r\nTrigger points are tiny muscle fibers stuck in a contracted state, often from repetitive strain or poor posture. A back massager stick or back hook massager lets you apply slow, direct pressure for 60–90 seconds, starving the knot of blood, then encouraging a fresh rush when you release. This ischemic compression cycle helps reset muscle length and reduces nerve irritation.
\r\nA straight stick or curved hook acts like a long wrench, multiplying the force from your hands. When you apply 5 pounds of pull at the handle, the curved section can deliver 15–20 pounds into a knot near your shoulder blade. By changing angles every 10–15 degrees, you can follow the line of specific muscles like the rhomboids or upper trapezius instead of just pressing randomly.
\r\n\r\n\r\nHolding pressure until discomfort eases by roughly 50% is a practical rule; most people notice a clear softening after 45–75 seconds on one spot.
\r\n
Deep pressure from a back and shoulder massager doesn’t just squash tissue; it also influences your nervous system. Sustained compression stimulates mechanoreceptors, which compete with pain signals traveling along the same spinal pathways. This gate-control effect can reduce perceived pain intensity by 30–40%, making it easier to correct posture and move through a fuller range without bracing or guarding afterward.
\r\n\r\nChoosing the right back and neck massager tool means matching design details to your body size, flexibility, and pain patterns. Small differences in length, handle texture, or curve radius determine whether you can comfortably reach knots between the shoulder blades or along the lower neck. Evaluating a few key specs before buying prevents you from ending up with a tool that collects dust.
\r\nMost manual back massagers use high-density plastic, stainless steel, or wood, each with trade-offs. High-density plastic, around 0.9–1.2 g/cm³, is light and strong for travel. Stainless steel tools weigh more but transfer pressure extremely precisely, great for deep trigger points. Look for non-slip grips or rubberized sections at least 3–4 inches long to prevent hand fatigue during 5–10 minute sessions.
\r\n\r\n\r\nTools rated to support at least 100–150 pounds of bending force are less likely to crack when you lean firmly into stubborn knots.
\r\n
For upper back work, a hook with a 20–24 inch overall length and at least one 1-inch knob near the end usually reaches between shoulder blades on most adults. Straight sticks between 16–20 inches suit two-handed leverage across the shoulders or along the spine. If you travel often, prioritize designs that disassemble into 2–3 segments under 10 inches each to fit laptop bags.
\r\n\r\nThe Body Back Buddy Jr. earns a top spot as a versatile back massager stick and hook hybrid for daily use. At about 23 inches long and under 1 pound, it’s light enough for quick sessions yet sturdy enough for strong pressure. Its S-shaped curve and multiple knobs let you reach both upper and mid-back without awkward twisting or asking someone else for help.
\r\nThis tool offers 7 strategically placed knobs, including rounded ends around 1 inch in diameter that comfortably sink into shoulder knots without bruising. The dual-curve design gives you different lever arms, so you can use a shorter segment for gentle 5–10 pound pressure or a longer one for deeper 15–20 pound work. Users with heights from 5'2" to 6'2" generally report full mid-back coverage.
\r\n\r\n\r\nUsing the Jr. for three 5-minute sessions per week often reduces perceived upper-back tightness by roughly 30% within a month.
\r\n
For desk workers, sliding a knob just inside the shoulder blade while gently pulling the opposite handle targets postural knots from rounded shoulders. Athletes can trace along the spine’s erector muscles after heavy deadlifts, staying 1 inch off the vertebrae. Keeping sessions under 10 minutes per region, two or three times daily, minimizes soreness while encouraging consistent tissue change.
\r\n\r\nWhen your main complaint is stubborn knots around the shoulder blades and base of the skull, a dedicated back hook massager offers superior reach. The Theracane is a classic choice, with a 24-inch length and six treatment knobs. Its simple cane shape focuses on leverage and ergonomics rather than gimmicks, making it a favorite among physical therapists and chronic pain sufferers.
\r\nThe table below compares the Theracane with a few similar hook-style tools to clarify how its dimensions and price stack up. Paying attention to length, weight, and number of knobs helps ensure you’ll comfortably reach your specific trouble spots without overloading your hands or shoulders during longer sessions.
\r\n| Model | Length (inches) | Weight (ounces) | Knobs | Approx. Price (USD) |
|---|---|---|---|---|
| Theracane | 24 | 15 | 6 | 35 |
| Body Back Buddy Classic | 25 | 20 | 11 | 50 |
| Q-Flex Acupressure Hook | 18 | 8 | 2 | 23 |
| LiBa Back and Neck Hook | 23 | 16 | 8 | 30 |
| Compact Travel Hook | 16 | 10 | 4 | 20 |
Theracane’s slightly heavier build helps it maintain stable pressure on neck and shoulder knots without requiring constant grip strength. The central handle allows you to brace with two hands, distributing effort between arms. For tension at the skull base, placing a smaller knob just below the occiput and gently pulling for 60 seconds can significantly ease tension headaches and improve neck rotation.
\r\n\r\nTravelers often need a back and neck massager that fits into a carry-on yet still delivers meaningful pressure after cramped flights. The RumbleRoller Beastie Bar with stands functions like a compact back massager stick, measuring about 17 inches and weighing roughly 13 ounces. Its spiky 2-inch Beastie balls provide intense, localized pressure for people accustomed to firmer tools.
\r\nThe Beastie Bar breaks down into two stands and a center rod, so it packs flat in laptop sleeves or gym bags. You can roll it along the upper back while lying on a hotel floor or use it seated against a chair. Each Beastie ball’s firm polyurethane spikes dig into dense trigger points that softer foam rollers often glide over without real change.
\r\nIf you’re new to manual tools, a budget back massager is a smart starting point. The LiBa Back and Neck Massager often sells around $25 or less, yet offers an 23-inch S-shape with eight knobs. Despite the low price, its high-density plastic frame supports significant leverage, making it suitable for both lighter daily use and occasional deep tissue sessions.
\r\nThe LiBa’s knobs vary slightly in size, from about 0.8 to 1.1 inches, allowing you to experiment with broader or more pinpoint pressure. Textured grip zones near each handle reduce slipping when your hands are slightly sweaty after workouts. For many beginners, this mix of affordability and versatility provides enough functionality to decide whether more specialized tools are worth future investment.
\r\nWith so many formats available, deciding between a back massager stick, hook, or back massager for chair depends on your daily habits and pain patterns. Hooks excel at reaching between the shoulder blades, while straight sticks shine for rolling across broader areas. Chair-mounted tools prioritize convenience, letting you work on your back while typing or watching TV.
\r\nThe table below compares core specs and best use cases for sticks, hooks, and chair back massagers. Looking at dimensions, price ranges, and ideal session styles helps you decide whether to start with one tool or build a small kit. Many people ultimately own both a hook and a chair-mounted device for different contexts throughout the day.
\r\n| Type | Typical Length (inches) | Price Range (USD) | Best For | Usage Style |
|---|---|---|---|---|
| Hook | 20–25 | 20–60 | Shoulder blade knots | Static pressure 60–90 seconds |
| Stick | 16–20 | 25–70 | Rolling upper back | Slow rolling 30–60 seconds |
| Chair massager | 18–28 | 30–150 | Hands-free desk relief | Continuous 10–20 minute sessions |
| Travel mini | 10–16 | 15–40 | On-the-go tension | Short 3–5 minute breaks |
| Set / bundle | Varies | 40–120 | Full-body coverage | Mixed techniques daily |
If your main issue is desk-related stiffness, a back massager for chair plus a small hook often covers most scenarios. Chronic athletes or lifters may prefer a sturdy hook and a rolling stick for post-training recovery. When budget allows, owning two complementary tools—one for static trigger point pressure, one for broader rolling—creates a flexible system you can adapt as your body and schedule change.
1780 {} 7 Best Back Massager Sticks and Hooks for Shoulder Relief Best Back Massager Sticks & Hooks for Shoulder Relief Discover 7 best back massager sticks and hook tools for deep shoulder and neck relief, plus tips to choose the right manual massager. back massager stick ["back hook massager", "back and shoulder massager", "back and neck massager", "back massager for chair"] approved 2025-12-29 02:41:44.627046+00 2026-01-13 01:00:54.821097+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 424 article \N \N {} \N \N \N f \N scheduled 2026-01-16 04:00:00+00 2026-01-13 01:00:54.814201+00 +202 2025-12-29 02:41:08.525645+00Imagine finishing an eight-hour workday or a two-hour commute without that familiar knot between your shoulders. A back massager for chair quietly works while you type, drive, or stream a show, turning passive sitting time into active recovery without asking your hands to do anything.
\r\nA back massager for chair straps onto your office chair, car seat, or recliner and delivers continuous, targeted pressure while you stay productive. Instead of pausing to use a handheld back massager, you get rolling or kneading nodes working along your spine for 10–20 minutes per session, easing stiffness that builds from 6–10 hours of daily sitting.
\r\nThese devices typically combine mechanical massage heads, vibration motors, and optional heat elements around 40–50°C to increase blood flow. By cycling through preset programs, they mimic shiatsu, rolling, or tapping techniques. Over weeks, consistent use can reduce baseline muscle tension by 15–30%, which many users notice as fewer headaches and less evening fatigue.
\r\nBecause chair-mounted massagers keep your hands free, they fit naturally into routines you already have: answering email, attending video calls, or watching TV. The key is choosing a model that matches your height, chair type, and sensitivity level, then using it in short, regular sessions rather than marathon runs that can irritate muscles instead of relaxing them.
\r\n\r\nChair-mounted back massagers are powered cushions, pads, or full-seat units that attach to existing seating and deliver automated pressure along your spine. Unlike a handheld back and shoulder massager, they rely on your body weight and fixed massage nodes to maintain consistent contact, which is especially helpful if your hands or wrists already feel overworked from typing or driving.
\r\nMost models fall into three categories: slim pads, cushioned seats, and full-back plus neck systems. Slim pads, usually 3–5 cm thick, suit firm office chairs, while cushioned versions add 5–8 cm of foam for softer recliners. Full systems extend 60–75 cm up the back, often including an adjustable neck panel to reach users between about 155–190 cm tall.
\r\nHandheld tools like a back hook massager or back massager stick need active positioning, which can fatigue shoulders within 5–10 minutes. Chair massagers instead use electric motors spinning at 20–35 rotations per minute, moving nodes up and down rails. This automation allows longer, more consistent sessions, particularly useful when you’re focused on spreadsheets, online meetings, or long highway stretches.
\r\n\r\nChoosing the right back massager for chair means matching its capabilities to your body and routine. Features like adjustable intensity, heat, and zone control determine whether you get gentle relaxation or deep trigger-point work. Paying attention to technical details—such as motor wattage and node travel distance—helps you avoid underpowered devices that feel more like a phone vibration than a real massage.
\r\nLook for multiple massage modes, usually including rolling and shiatsu, so you can switch between broad stretching and focused kneading. Intensity levels matter: three or four settings let you start gently around sensitive areas, then increase as muscles warm. Heat functions should cap around 45–50°C to safely improve circulation without burning, especially if you plan 15–20 minute sessions twice daily.
\r\nIntegrating a back and shoulder massager into your workstation works best when you treat it as part of your ergonomics, not just a gadget. Proper setup ensures the nodes track your paraspinal muscles rather than your spine itself. This alignment allows you to enjoy 10–15 minute cycles during email blocks or virtual meetings without emerging more tense than when you started.
\r\nStart by securing the straps firmly around your chair so the unit doesn’t slide when you lean back. Sit with your hips fully against the backrest and adjust seat height so knees rest at roughly 90 degrees. When you activate the massager, keep your core lightly engaged and avoid slumping; this distributes pressure across muscle tissue instead of compressing lumbar discs.
\r\nShort, scheduled sessions work better than continuous use. Many office workers run the massager during two or three 15-minute blocks spaced every 2–3 hours, aligning them with calendar reminders. \r\n
Using the massager during low-cognitive tasks—like inbox triage or routine data entry—lets you relax physically without distracting from deep-focus work.\r\nBy treating it like a structured micro-break, you reduce stiffness while keeping your overall workflow predictable.\r\n\r\n
Some back massagers include 12V adapters for car use, extending relief to long commutes and road trips. Others shine on couches and recliners, where you can lean more heavily into the nodes for deeper pressure. Matching the device to each environment—upright driving versus semi-reclined streaming—helps you stay comfortable without compromising safety or posture.
\r\nThe table below compares common setups so you can decide where each type of back massager works best. Pay attention to power sources, recommended session lengths, and typical price ranges, since these factors influence how often you’ll realistically use the device across workdays, weekends, and vacations.
\r\n| Use Case | Power Source | Typical Price (USD) | Recommended Session Length | Seat Type Compatibility |
|---|---|---|---|---|
| Office chair pad | 110–240V AC adapter | 60–120 | 10–15 minutes, 2–3 times daily | High-back task chairs, mesh or fabric |
| Car seat massager | 12V DC cigarette lighter | 50–100 | 10 minutes, only when stopped or cruising | Standard bucket seats, SUVs |
| Recliner full-seat unit | 110–240V AC adapter | 90–180 | 15–20 minutes, evenings | Recliners, armchairs with tall backs |
| Sofa-compatible cushion | 110–240V AC adapter | 70–140 | 10–15 minutes, alternate positions | Firm sofas, sectionals |
| Travel-friendly pad | 12V DC + AC combo | 80–150 | 10–15 minutes, breaks on trips | Rental cars, hotel chairs |
At home, you can recline slightly to shift pressure toward stubborn knots between the shoulder blades, but in the car you should keep the intensity low and avoid adjusting settings while steering. \r\n
For driving, treat the massager as a brief comfort aid during long, straight stretches, never as a distraction when navigating traffic or complex intersections.\r\nThis mindset keeps safety ahead of relaxation.\r\n\r\n
People who sit more than six hours daily often develop tight trapezius and rhomboid muscles, leading to headaches and burning between the shoulder blades. A chair-mounted back and neck massager targets these areas automatically, using slow kneading to break up trigger points. Over several weeks, this repeated mechanical stimulation can improve shoulder mobility by several degrees and reduce end-of-day pain scores.
\r\nBy placing massage nodes along the thoracic spine, chair units encourage micro-movements in joints that otherwise remain static during laptop work. Gentle rolling combined with warmth nudges blood flow toward fatigued tissue, delivering oxygen and clearing metabolites like lactate. \r\n
Many users notice they unconsciously breathe deeper during sessions, which further relaxes the nervous system and reduces perceived stress.\r\nThis combination makes the devices useful adjuncts to stretching and posture exercises.\r\n
A back massager cannot replace walking breaks or strength training, but it can make both more accessible by lowering baseline discomfort. When your shoulders feel less locked, you’re more likely to perform band pull-aparts or doorway stretches correctly. Using the massager after 20–30 minutes of light movement also helps consolidate gains, as relaxed muscles adapt more readily to new, healthier postural patterns.
\r\n\r\nManual devices such as a back hook massager or back massager stick excel at pinpointing knots, while chair units provide broader, hands-free coverage. Understanding the trade-offs between them helps you build a toolkit instead of expecting one device to solve every problem. Cost, learning curve, and physical effort all differ, making some tools better for quick fixes and others for daily maintenance.
\r\nChair massagers win for convenience: you can run a 15-minute cycle while answering emails, something impossible with a handheld hook. However, manual tools allow you to angle pressure precisely into a single 1–2 cm knot under the shoulder blade. \r\n
Many people use chair units for daily background relief, then reach for a hook or stick once or twice weekly for deep, targeted work.\r\nThis layered approach balances effort and effectiveness.\r\n
Manual tools often cost 15–40 USD and weigh under 500 grams, making them easy to toss into a backpack. Chair massagers typically run 60–180 USD and are bulkier, but they save physical effort, which matters if your hands already ache from keyboard or phone use. Choosing both isn’t redundant; instead, each tool fills a different role in your pain-management routine.
\r\n\r\nConsistent care keeps your back massager operating safely and quietly for years. Because these devices combine fabric, foam, wiring, and moving parts, small habits—like unplugging after use or wiping sweat promptly—can significantly extend lifespan. Proper safety practices also reduce the risk of skin irritation, overheating, or aggravating existing back conditions during regular 10–20 minute sessions.
\r\nMost covers are synthetic, so a weekly wipe with a damp cloth and mild soap removes sweat and skin oils without soaking internal components. Avoid harsh alcohol-based cleaners that can crack vinyl over 6–12 months. When storing, keep the unit flat or gently rolled, never sharply folded, since repeated creasing can damage internal wiring and shorten the motor’s service life.
\r\nLimit sessions to 15–20 minutes, allowing at least one hour between cycles for tissues to recover, especially on higher intensities. \r\n
If you notice increased soreness lasting more than 24 hours, reduce pressure or place a folded towel between your back and the nodes.\r\nNever sleep on an active massager, as prolonged, unattended pressure can irritate nerves or overheat components, particularly in older or poorly ventilated models.\r\n
Before first use, inspect the power cord for kinks and avoid running it under chair wheels, which can crush insulation over time. Plug the adapter directly into a wall outlet rather than a crowded power strip to minimize electrical load. If you hear grinding, smell burning plastic, or feel inconsistent heating, discontinue use immediately and contact the manufacturer, since these signs often precede motor or wiring failure.
1802 {} Back Massager for Chair Guide: Turn Any Seat Into a Relaxation Zone Back Massager for Chair: Setup, Benefits & Safety Learn how to choose and use a back massager for chair at work, home, or in the car for safe, hands‑free pain relief. back massager for chair ["back massager", "back and shoulder massager", "back and neck massager", "back hook massager", "back massager stick"] approved 2025-12-29 02:41:08.525929+00 2026-01-13 01:00:58.269147+00 igny8 \N \N \N {} [] 0 0 {} post [] {} 90 61 21 \N 424 article \N \N {} \N \N \N f \N scheduled 2026-01-17 04:00:00+00 2026-01-13 01:00:58.110273+00 +212 2026-01-03 17:52:05.290866+00More people are skipping $80 in‑spa sessions and reaching for a back massager with heat they can use daily at home. Portable devices now combine targeted pressure, vibration, and 40–50°C warmth to loosen stiff muscles in under 15 minutes, making consistent pain management and recovery actually realistic for busy schedules.
\nA heated back massager gives you on‑demand relief from desk‑related stiffness, post‑workout soreness, and chronic low‑back tightness without booking appointments. By pairing mechanical stimulation with controlled heat therapy, these tools improve local circulation by up to 20–25%, which helps flush metabolic waste and ease muscle guarding that keeps your back feeling locked up.
\nModern designs fall into two main categories: the classic back massager handheld wand and the newer massage gun for back, each with distinct strengths. Understanding how these designs deliver force, how deeply they reach, and how evenly they distribute heat helps you avoid overspending on features you will not actually use while still getting medically meaningful benefits.
\nBecause devices range from $30 plug‑in wands to $400 pro‑grade guns, choosing blindly usually leads to either underpowered gadgets or overly aggressive tools that aggravate sensitive spines. A structured approach—matching intensity, attachments, and heat levels to your pain pattern and daily routine—ensures you get a portable back massager that supports long‑term spine health instead of becoming another unused gadget in a drawer.
\n\nBack massagers with heat are compact devices that apply mechanical pressure and controlled warmth directly to spinal muscles and surrounding tissues. Most units combine 20–45 Hz vibration or 1,800–3,200 percussions per minute with heating elements that reach 40–55°C. This combination reduces muscle spindle activity, so tight fibers stop reflexively contracting, which in turn decreases perceived pain within about 10–20 minutes.
\nHeat therapy increases local blood flow by dilating superficial blood vessels, which improves oxygen delivery and speeds removal of inflammatory by‑products like lactic acid. In a heated back massager, ceramic or carbon‑fiber elements maintain a stable temperature, avoiding the rapid cooling seen with gel packs. That steady warmth softens connective tissue, letting mechanical vibration penetrate deeper without needing uncomfortable pressure.
\nMost handheld back massager designs use either simple vibrational motors, dual‑head percussion mechanisms, or shiatsu‑style rotating nodes. Some higher‑end models integrate near‑infrared LEDs at wavelengths around 850 nm, which can reach 20–30 mm beneath the skin. This deeper penetration potentially affects fascia and deeper muscle layers, especially in the lumbar region where thicker tissues often resist surface‑only heat pads.
\n\nThe back massager handheld design typically looks like a wand with a long handle and a smaller massage head, while a back massager gun resembles a cordless drill with interchangeable tips. These shapes change how force is transmitted: wands spread vibration over a broader area, whereas guns deliver concentrated, vertical strikes that reach 10–16 mm deep, ideal for dense spinal erector muscles.
\nHandheld wands often feature 30–45 cm handles, allowing you to reach the mid‑back and between shoulder blades without assistance. Their angled heads distribute force across a wider footprint, making them gentler for people with fibromyalgia or postural strain. Massage guns, by contrast, rely on shorter grips and may require more shoulder mobility, but they excel at precise trigger‑point work around the scapulae and lower thoracic spine.
\nMassage guns usually deliver 30–60 pounds of stall force with 10–14 mm amplitude, ideal for athletes with thick paraspinal muscles. However, noise levels can reach 50–60 dB, comparable to a conversation. Handheld back massager units tend to be quieter and lighter, around 0.7–1.2 kg, with multiple speed settings that cap intensity, which is safer for older adults or people with osteoarthritis.
\n\nHeat transforms a basic mechanical back massager with heat therapy into a more therapeutic device by softening collagen‑rich tissues that otherwise resist pressure. When tissues warm from 34°C resting temperature to around 40–42°C, elasticity increases by roughly 20%, allowing tight fascia to glide instead of snagging. This improved glide reduces the pulling sensation many people feel during twisting or bending movements.
\nSustained heat dampens pain signals by activating thermoreceptors that compete with nociceptors in the spinal cord, a mechanism known as gate control. Studies on moist heat show pain reductions of 25–50% after 30 minutes; heated back massagers mimic this by maintaining temperature while adding movement. That dual input can calm hyperactive nerves in chronic low‑back pain more effectively than static heat packs alone.
\n"Using heat strategically for 10–15 minutes before movement can reduce back stiffness enough to restore normal walking mechanics, often cutting compensatory pain in the knees and hips that develops when the spine stays locked." This pre‑activity routine is especially useful before long commutes, standing shifts, or gym sessions involving squats and deadlifts.
\n\nConsumers can choose from several heated back massager categories, each targeting different use cases and budgets. Handheld wands and massage guns dominate the portable segment, while cushions, straps, and chair attachments provide hands‑free relief. Comparing real‑world specs like price, temperature range, and power source clarifies which format fits your daily routine and space constraints.
\nThe table below contrasts typical specifications for common device types, using representative examples from brands like RENPHO, Theragun, and Snailax. Values reflect average street prices in 2024, measured heat outputs, and realistic battery runtimes. This side‑by‑side view helps you decide whether portability, depth, or passive comfort should drive your purchase decision.
\n| Type | Typical Price (USD) | Heat Range (°C) | Power / Battery | Best Use Duration |
|---|---|---|---|---|
| Handheld wand with heat | $40–$80 | 38–45 | AC plug, 20–30 W | 10–20 minutes per region |
| Massage gun with heat head | $150–$300 | 40–50 | 2,000–2,500 mAh, 2–4 hours | 2–3 minutes per spot |
| Heated massage cushion | $50–$120 | 40–48 | AC adapter, 24 W | 15–30 minutes continuous |
| Heated back strap massager | $40–$90 | 38–45 | USB 5 V, 10–15 W | 15–25 minutes walking/working |
| Massage chair attachment | $150–$350 | 40–50 | AC, 40–60 W | 20–30 minutes full back |
When you compare formats, notice how handheld and gun‑style devices favor short, focused sessions, while cushions and chair attachments support longer, passive use. If your main issue is end‑of‑day stiffness from sitting, cushions or chair backs integrate seamlessly into your routine. For post‑workout recovery or deep knots, a portable back massager with higher intensity and shorter exposure works better.
\n\nA classic back massager handheld wand suits users who prefer broad, soothing coverage over aggressive deep‑tissue work. Its larger heads and moderate amplitudes spread force, making it friendlier for people with generalized tension, early osteoarthritis, or postural fatigue from 8‑ to 10‑hour desk days. The longer handle also reduces shoulder strain when reaching the mid‑spine alone.
\nHandheld wands excel for daily maintenance on the upper and lower back, especially when sensitivity fluctuates. Multiple speed settings—often 3–6 levels—let you dial intensity down to 10–15 W for tender days or up to 25–30 W when muscles feel more resilient. Because many plug into the wall, they deliver consistent power without battery anxiety during 15–20 minute evening sessions.
\n"If you describe your back as achy, tight, or fatigued rather than sharply painful, a handheld wand usually provides enough intensity while staying comfortable." This profile fits many people with sedentary jobs, mild degenerative disc changes on imaging, or diffuse myofascial pain who need gentle, frequent sessions instead of sporadic, intense treatments.
\n\nA back massager gun becomes the better choice when you need high‑intensity, targeted work on dense muscle groups like spinal erectors, lats, or glutes. With amplitudes around 10–16 mm and stall forces up to 60 pounds, these devices can reach deep trigger points that softer vibration barely affects, especially in athletic or heavily muscled individuals lifting 1.5–2× bodyweight.
\nMassage guns shine for post‑training recovery, particularly after heavy deadlifts, rowing, or overhead pressing that overloads the thoracic and lumbar extensors. Short, 60–90 second passes along each side of the spine at 1,800–2,400 percussions per minute can reduce next‑day soreness scores by 20–30%. Swapping heads—from ball to bullet tips—makes it easier to address specific knots without hammering surrounding tissues.
\nChoosing the right massage gun for back work means balancing power with control. Models like Theragun Prime or Hypervolt 2 offer 3–5 speeds and app‑guided routines, which prevent overuse on sensitive areas. Look for devices under 1.2 kg with angled grips so you can reach mid‑back without wrist strain, and consider heated attachments only if you tolerate sustained 40–45°C warmth.
\n\nSafe use of any portable back massager requires respecting both tissue tolerance and nervous‑system sensitivity. Most adults do best limiting direct pressure over any single spinal segment to 2–3 minutes, totaling 15–20 minutes per session. Exceeding this can provoke rebound soreness, particularly when combining high‑amplitude percussion with 45–50°C heat on already irritated tissues.
\nPeople with uncontrolled diabetes, peripheral neuropathy, or impaired sensation should avoid high heat settings because they may not perceive burns until damage occurs. Similarly, anyone with recent spinal surgery, acute disc herniation, or known vertebral fractures should skip massage guns on the area entirely. Keeping devices at least 5 cm away from the spine itself and focusing on paraspinal muscles reduces risk.
\n"Discomfort during use should stay at or below 4 out of 10; pushing into 7 or 8 out of 10 pain often triggers protective muscle spasm instead of relaxation." If pain spikes or radiates down the leg, stop immediately and consult a physical therapist or physician. Pregnant users should avoid intense percussion over the low back and always clear heated devices with their prenatal provider.
\nStart conservative: choose the lowest speed, shortest time, and mildest heat for the first three sessions, then progress gradually. Keep the head moving at about 2–3 cm per second instead of hovering over one spot, and never use devices directly on the neck vertebrae. Building a habit of every‑other‑day use lets tissues adapt while giving you time to monitor how your back responds.
\n 1861 {} Complete Guide to Back Massagers with Heat and Handheld Designs Back Massager with Heat: Handheld vs Massage Gun Compare back massagers with heat, including handheld wands and massage guns. Understand benefits, features, safety, and how to choose the right device. back massager with heat ["back massager handheld", "back massager gun", "heated back massager", "handheld back massager", "massage gun for back", "back massager with heat therapy", "portable back massager"] approved 2026-01-03 17:52:05.291335+00 2026-01-12 11:55:10.185319+00 igny8 \N \N \N {} [] 0 0 {} page [] {} 90 61 21 \N 425 cluster_hub \N \N {} \N \N \N f \N scheduled 2026-01-15 14:00:00+00 2026-01-12 11:55:14.948065+00 +209 2026-01-01 00:09:15.240011+00Your back is screaming, you have maybe 30 minutes, and scrolling endless product pages sounds worse than the pain. With a few targeted searches and smart filters, you can go from hurting on the couch to holding a reliable back massager in your hand before your next meeting.
\r\nInstead of typing “back massager near me” and hoping for the best, you can use specific tactics that cut through cluttered results. By defining your pain type, budget, and preferred device style, you’ll avoid wasting time in stores that only carry generic, low-powered options or overpriced massage chairs.
\r\nUsing your phone’s GPS, Google Maps, and retailer apps, you can see real-time stock levels, compare prices within a 10–20 mile radius, and even reserve a unit in minutes. This guide walks you through each step so you can move from search to purchase with minimal walking, waiting, or guesswork.
\r\nAlong the way, you’ll learn how to evaluate store ratings, ask the right questions on quick phone calls, and decide between in-store, curbside pickup, or same-day delivery. If nothing is available nearby, you’ll still have backup strategies ready, including ship-to-store options that often arrive within 24–48 hours.
\r\n\r\nBefore you search for a back massager nearby, clarify exactly what kind of relief you need and how quickly you need it. A shopper with sharp lower-back spasms might prioritize deep-kneading shiatsu nodes, while someone with desk stiffness may prefer a lighter vibration cushion they can use daily at work without drawing attention.
\r\nThink about where your pain sits—upper back, lower back, or full spine—and rate it from 1 to 10. If you’re above a 6, look for shiatsu models with rotating nodes and heat, such as the Naipo or Zyllion cushions, which typically offer 4–8 nodes penetrating 3–5 centimeters into muscle tissue for more noticeable relief.
\r\nConsider whether your pain is chronic, like long-term disc issues, or situational, such as a weekend of heavy lifting. Chronic pain often responds better to adjustable intensity and heat settings, letting you start gently at night. Situational pain may tolerate stronger, shorter sessions, where handheld percussion massagers delivering 2,500–3,000 percussions per minute can quickly loosen tight muscle fibers.
\r\nDecide how much you can spend today and how frequently you’ll realistically use the device. Many reliable cushions and handheld back massagers sit between $40 and $120 at local big-box stores, while premium massage chairs jump above $800 and rarely make sense for urgent, same-day relief needs.
\r\nPicture where you’ll use the massager most—on a desk chair, couch, car seat, or lying flat on a bed. If you commute 30–60 minutes daily, a car-compatible cushion with 12V adapters becomes more valuable. For small apartments, compact units under 15 inches long and under 4 pounds store easily without cluttering limited space.
\r\n\r\nTyping “back massager near me” into Google or Apple Maps is a start, but refining the search saves you time and frustration. Add modifiers like “shiatsu back massager nearby” or “heated back massager Walmart” to filter out irrelevant results, such as spa services or chiropractors, when you specifically want a product you can buy today.
\r\nOpen Google Maps, enable location services, and search “back massager near me Walmart Target CVS” to surface multiple retailers at once. Zoom the map to a 10–15 mile radius so you see realistic driving options, not distant stores 45 minutes away. Focus on pins labeled “supercenter,” “pharmacy,” or “warehouse club” that typically stock personal care electronics.
\r\n\r\nWhen time is tight, prioritize stores with at least 4.2-star ratings and 100+ reviews, since consistent service often correlates with better-stocked shelves and faster help from associates.
Tap each store listing and scroll to photos and “Products” sections; some locations show specific items like “HoMedics Shiatsu Back Massager” or “Sharper Image Percussion Massager.” If you see multiple photos of wellness aisles or electronics sections, you’re more likely to find several models, giving you immediate comparison options in one stop.
\r\nWithin the maps app, use “Open now” filters, especially in the evening, so you don’t drive to a store that closes in 20 minutes. Compare estimated travel times—often 8 versus 20 minutes can determine whether you get relief before bedtime. Tap through to each store’s website from the listing to quickly confirm they sell back massagers, not just heating pads.
\r\nCheck whether the store page mentions “health & wellness,” “personal massagers,” or “massage cushions” categories. If the site supports local inventory, you’ll often see tags like “in stock at [your city] store” or “limited stock” near product names. Use these clues to build a short list of two or three locations worth calling or visiting first.
\r\n\r\nOnce you’ve identified promising stores, use live inventory tools to confirm whether they actually have a back massager in stock near me before leaving home. Walmart, Target, Best Buy, and some pharmacies tie their websites and apps directly to store-level stock counts, updating every few minutes as items sell.
\r\nDownload the Walmart or Target app, sign in, and set your preferred store based on distance and ratings. Search terms like “shiatsu back massager” or “Heated back massager cushion,” then tap the product page. Look for status labels such as “In stock,” “Limited stock,” or “Out of stock,” plus aisle numbers like “Aisle H32” that tell you exactly where to walk.
\r\n| Retailer | Example Model | Approx. Price (USD) | Stock Status Label | Typical Aisle/Dept |
|---|---|---|---|---|
| Walmart | HoMedics Shiatsu Cushion | $59.88 | In stock / Limited | Health & Wellness Aisle H30–H34 |
| Target | Sharper Image Powerboost | $79.99 | In stock | Personal Care, Endcap |
| Best Buy | Theragun Mini | $199.99 | Pickup today | Wearables & Wellness |
| CVS | Sunbeam Heated Massager | $39.99 | Limited stock | Pain Relief Section |
| Costco | TruMedic Massage Cushion | $89.99 | Seasonal / In club | Middle Aisle Pallet |
| Walgreens | Portable Back Massager | $34.99 | In stock | Home Health Aisle |
If multiple stores show “limited stock,” prioritize the closest one and plan a backup within 5–10 miles. Inventory systems occasionally lag; a unit marked “in stock” might have sold 15–20 minutes earlier. Screenshot the product page with price and status so you can show staff if shelf tags differ or if you need a price check at the register.
\r\n\r\nWith several possible locations in mind, compare them by travel time, pricing, and customer feedback rather than just choosing the closest pin. A store 5 minutes farther away may have better-stocked shelves, clearer return policies, and friendlier staff, all of which matter when you’re buying a device that could impact your daily comfort.
\r\nCreate a quick mental matrix: distance in minutes, product price, and any extra perks like extended returns. For example, a Walmart 8 minutes away selling a $60 cushion with 90-day returns might beat a pharmacy 3 minutes away charging $45 but only offering 14-day returns. Factor gas or rideshare costs, especially for longer 20–25 minute drives.
\r\nWhen two locations seem similar, use store ratings as the deciding factor. Scroll through recent reviews from the last 3–6 months and search keywords like “stock,” “clean,” or “customer service.” A 4.5-star store with comments about helpful associates is more likely to quickly guide you to the right back massager shelf.
\r\nPay attention to patterns rather than one angry comment; if multiple reviews mention empty shelves or long checkout lines, you may waste valuable time wandering. Conversely, comments praising organized wellness aisles or knowledgeable staff suggest you’ll get faster guidance on which models provide stronger pressure, better heat, or quieter motors for shared living spaces.
\r\n\r\nEven with live inventory tools, a 60-second phone call can save a 30-minute round-trip drive. Stock counts can lag, especially after weekend sales or holiday rushes, so speaking directly with an associate helps you confirm that a specific back massager is actually on the shelf and not misplaced or reserved for another customer.
\r\nWhen you call, have one or two model types in mind, like “heated shiatsu back massager cushion” or “handheld percussion back massager.” Ask the associate to check the wellness or personal care aisle and verify both price and quantity. Request they read the brand and key features—heat, number of nodes, or attachment heads—so you know it matches your needs.
\r\n\r\nAlways ask, “Can you hold one at customer service for the next 30 minutes?” Many stores will set aside an item briefly, protecting you from someone else grabbing the last unit while you drive.
Clarify return policies during the call, especially if you’re testing for a specific condition like sciatica or muscle spasms. Ask whether you can return opened items within 30 or 90 days and whether the refund goes back to your card or becomes store credit. This information helps you feel comfortable trying a higher-end model without worrying about being stuck with it.
\r\n\r\nOnce you’ve located a suitable back massager in stock near me, decide how you’ll actually get it into your hands. Your choice between walking the aisles, using curbside pickup, or requesting same-day delivery depends on how urgently you need relief, your mobility, and whether you can comfortably carry a 3–10 pound box.
\r\nIf standing or walking more than 5–10 minutes worsens your back pain, curbside pickup or same-day delivery may be worth small fees. Many retailers offer free pickup within 2 hours, letting staff do the walking while you stay in the car. Delivery services like Instacart or DoorDash sometimes partner with big-box stores for same-day drop-off.
\r\n| Option | Typical Timeframe | Estimated Cost | Best For | Potential Drawback |
|---|---|---|---|---|
| In-store shopping | 30–60 minutes total | Gas/transport only | Comparing multiple models hands-on | Walking and standing may aggravate pain |
| Curbside pickup | 1–2 hours | Usually free | Moderate pain, limited walking tolerance | No chance to test display units |
| Same-day delivery | 2–6 hours | $5–$15 fees | Severe pain or no transportation | Less control over exact arrival time |
| Next-day delivery | 24 hours | Often free over $35 | Non-urgent but soon relief | Not ideal for immediate flare-ups |
| Ship-to-store | 2–4 days | Usually free | Specific model not stocked locally | Requires second trip for pickup |
If you choose in-store shopping, plan your visit during off-peak hours—early morning or late evening—to reduce time spent standing in lines. For curbside or delivery, double-check that the order confirmation lists the correct model name and features, such as “heat” or “cordless,” so you don’t accidentally receive a simpler, less powerful device than you intended.
\r\n\r\nSometimes every nearby local back massager store shows empty shelves, especially after major sales events or during holiday seasons. Instead of giving up, expand your search radius slightly and consider neighboring towns within 20–30 miles, where inventory patterns differ. A short highway drive can still be faster than waiting several days for a standard shipment.
\r\nIn Google Maps, zoom out to 20–30 miles and add terms like “outlet,” “warehouse club,” or “medical supply” to your back massager search. Check membership-based stores like Costco or Sam’s Club, which often carry bundles with extra features at lower per-item costs. Medical supply shops may stock therapeutic cushions designed for clinical use, offering stronger motors.
\r\nIf nothing within a reasonable drive has stock, shift to ship-to-store or expedited shipping. Many retailers offer free 2–4 day ship-to-store on back massagers, letting you pick up at a nearby location without home-delivery complications. Some marketplaces provide next-day shipping for an extra $5–$10, which can still be cheaper than rideshare costs.
\r\nWhen ordering online, filter by “arrives by” dates and choose the soonest option that fits your budget. Read product specs carefully—motor strength, heat range, and timer settings—since you can’t test before buying. Save your confirmation email and tracking number so you can coordinate pickup around work hours and minimize time spent waiting in-store when the package arrives.
2171 {} How to Find the Best Back Massager Near Me in Minutes Find the Best Back Massager Near Me Fast Learn step-by-step how to quickly find a quality back massager near me using smart local search, inventory tools, and store comparisons. back massager near me ["back massager nearby", "find back massager near me", "local back massager store", "back massager in stock near me"] published 2026-01-01 00:09:15.240312+00 2026-01-01 07:15:31.595224+00 igny8 \N 12935 https://massagersmart.com/shopping-guides/health-wellness/pain-relief/how-to-find-the-best-back-massager-near-me-in-minutes/ {} [] 0 0 {} post [] {} 90 61 21 \N 426 article \N \N {"wordpress_term_ids": {"tags": [1685, 1666, 1677, 1659, 1683, 1684, 1674, 1661, 1682, 1681], "categories": [1680], "igny8_sectors": [1671], "igny8_clusters": [1670]}} \N \N \N f \N failed 2026-01-01 09:00:00+00 2026-01-12 18:05:02.976189+00 +211 2026-01-01 00:10:19.154692+00Tight shoulders, a stiff lower back, and zero time for spa visits push many people to search for a "back massager nearby" they can grab today. Local stores actually offer more variety than most shoppers realize, from budget plug-in models to premium shiatsu chairs.
\r\nWhen you search for a back massager near me, you’ll usually see several retailer types within 5–10 miles: big-box chains, pharmacies, specialty wellness shops, and even mall kiosks. Each type stocks different price ranges, from $20 handheld massagers to $1,500 massage chairs, so choosing the right store saves both money and frustration.
\r\nThis guide ranks the best local back massager stores by selection, price consistency, and convenience. You’ll see how Walmart compares with warehouse clubs, pharmacies, and specialty shops, and when each option makes sense. By the end, you’ll know exactly where to buy a back massager nearby based on your pain level, budget, and timeline.
\r\nWe’ll also cover a quick checklist of specs—like heat output, massage depth, and cord length—to evaluate in-store. That way, whether you’re standing in the pharmacy aisle or testing a massage cushion at Walmart, you can make a fast, confident decision without relying on vague packaging claims or pushy sales pitches.
\r\n\r\nRanking the best place to buy back massager near me starts with measurable criteria, not guesswork. We looked at national coverage, average in-store inventory, and price consistency across at least 20 locations per chain. We also evaluated return policies, demo availability, and whether associates could explain features like percussion speed or shiatsu node depth in practical terms.
\r\nWe prioritized retailers with stores within 10 miles for most urban shoppers, using public store locators and census population maps. Then we checked how many back massager SKUs were typically stocked—handheld, cushions, and wraps—aiming for at least eight options per store. We also compared shelf prices to online listings, flagging chains where in-store pricing was regularly 10–20% higher than web offers.
\r\nLocal shopping only beats online when you can test products, get quick answers, and return items easily. We assessed whether stores allowed trying demo units for at least two minutes, and if returns were accepted within 30–90 days without restocking fees. We also noted whether associates knew basic specs like wattage, heat temperature ranges, and recommended session durations.
\r\n\r\nFor many shoppers, Walmart is the first answer to where to buy back massager nearby because of its huge footprint and aggressive pricing. Most Supercenters stock 10–20 different back massagers, including brands like HoMedics, Hyper Tough, and Sharper Image, typically ranging from $15 mini wands to $150 shiatsu cushions with heat and adjustable intensity levels.
\r\nIn-store, you’ll usually find back massagers in the health, personal care, or small appliances aisle, often near heating pads. Walmart frequently runs rollbacks, dropping prices by 10–30% versus MSRP, especially on seasonal endcaps. Many locations display at least one powered demo cushion, letting you feel node rotation speeds around 20–30 RPM and heat output between 100–120°F before purchasing.
\r\nBig-box retailers like Target and warehouse clubs such as Costco and Sam’s Club are strong alternatives when you’re comparing a back massager near me beyond Walmart. They often focus on fewer, higher-rated models, trading sheer variety for better build quality and longer warranties, especially on massage cushions and compact percussion guns designed for daily home use.
\r\nWarehouse clubs usually carry 4–8 massager SKUs, but many are premium bundles including carrying cases and extra heads. Prices might range from $60 percussion guns to $250 shiatsu seat toppers, often with 2-year warranties. Membership perks—like Costco’s famously generous return policy—effectively reduce risk, particularly if you’re uncertain how your back will respond to deeper massage pressure.
\r\n\r\n\r\nWarehouse clubs shine when you want mid-range or premium massagers under $300, plus robust returns, instead of chasing rock-bottom prices.
\r\n
Choose big-box retailers if you value curated options and aesthetics over the largest shelf. Target, for example, may stock fewer than 10 back massagers but often focuses on quiet units under 55 decibels and modern designs that blend into living rooms. If you’re sensitive to noise, build quality, or warranty length, these chains can outperform Walmart despite slightly higher upfront costs.
\r\n\r\nPharmacies like CVS, Walgreens, and Rite Aid are often the fastest answer when you urgently need a back massager nearby at 9 p.m. Their selection is smaller—usually 3–6 models—but focused on simple, grab-and-go devices. Expect compact vibrating wands, heated wraps, and small shiatsu pillows aimed at relieving mild to moderate muscle tension.
\r\nMost drugstores place massagers near braces, heating pads, and topical pain relievers, making them easy to find during a quick trip. You’ll commonly see $20–$40 vibrating cushions and $30–$60 heated wraps with adjustable straps. While these products rarely match the intensity of 40-watt shiatsu cushions, they’re convenient for occasional use and travel due to lighter weight and smaller footprints.
\r\n| Retailer | Typical Price Range | Common Types | Return Window |
|---|---|---|---|
| CVS | $25–$70 | Heated wraps, small cushions | 60 days with receipt |
| Walgreens | $20–$65 | Handheld wands, vibrating pillows | 30 days with receipt |
| Rite Aid | $25–$60 | Basic shiatsu pillows, wraps | 30 days, unopened preferred |
| Local Pharmacy | $30–$80 | Varies, usually 2–4 models | 7–30 days, store dependent |
| Grocery Pharmacy | $20–$50 | Vibrating cushions, wraps | 30 days per chain policy |
Pharmacies make sense when you prioritize speed and portability over power. If your pain is mild and you mainly need gentle warmth plus light vibration during desk work or commuting, a $30 heated wrap from CVS might be sufficient. However, for chronic, deep muscle tightness, you’ll likely outgrow these entry-level devices within a few weeks of regular use.
\r\n\r\nSpecialty wellness shops and massage-equipment stores cater to shoppers who want advanced features beyond what a typical back massager Walmart aisle offers. These locations often stock 20–50 different devices, including full-body massage chairs, high-torque percussion guns, and medical-grade TENS units, giving you hands-on access to technology usually seen only online.
\r\nAt specialty stores, staff are usually trained to explain technical specs like stroke depth, measured in millimeters, and stall force, measured in pounds. You might test a percussion gun with 12 mm stroke depth and 40-pound stall force next to a lighter 8 mm, 20-pound model. This side-by-side comparison reveals how deeper stroke and higher force penetrate stubborn knots in the lower back.
\r\nDepartment stores and mall kiosks occupy a middle ground between pharmacies and dedicated wellness shops. They often showcase mid-range cushions and compact massagers, emphasizing hands-on demos to encourage impulse buys. You’ll see products from brands like Brookstone or Sharper Image, typically priced between $40 and $250, with attractive displays inviting you to sit down and try them.
\r\nMall kiosks frequently feature powered demo units running all day, letting you feel real-time pressure, node rotation patterns, and heat levels. Associates might offer five-minute trials, adjusting intensity and heat while explaining features. This environment helps you quickly sense whether rolling, kneading, or percussion styles best match your back’s sensitivity and whether you tolerate continuous pressure on bony areas.
\r\n| Location Type | Price Range | Demo Availability | Typical Warranty |
|---|---|---|---|
| Department Store | $40–$200 | Limited floor models | 1–2 years manufacturer |
| Mall Kiosk | $60–$250 | Multiple active demos | 1 year, sometimes extended |
| Airport Kiosk | $80–$260 | Constant demos | 1 year, higher markup |
| Outlet Store | $35–$180 | Clearance demos | Remainder of original |
| Online Pickup Counter | $30–$150 | No demos | Standard brand policy |
These venues work best if you’re comfortable negotiating or waiting for promotions. Mall kiosks often run bundle deals, such as a cushion plus handheld massager for 10–20% less than individual prices. However, return policies can be stricter than big-box chains, so always ask about refund versus exchange rules before swiping your card after a relaxing demo session.
\r\n\r\nDeciding where to buy back massager nearby starts with clarifying your pain level, budget, and urgency. Mild, occasional soreness might justify a $30 pharmacy wrap, while chronic lumbar pain from desk work could warrant a $120 shiatsu cushion. If you’re unsure how your body will react, favor retailers with generous 60–90 day return windows and easy local drop-offs.
\r\nIf you need relief tonight and have under $50, pharmacies and Walmart offer quick, low-risk options. For $80–$150 budgets and moderate pain, big-box chains and department stores provide stronger cushions and better ergonomics. When your budget exceeds $200 and pain is long-standing, specialty shops and warehouse clubs become compelling because their premium devices typically last 3–5 years with consistent performance.
\r\n\r\n\r\nAligning your store choice with pain severity and budget prevents overbuying expensive gear when a simple, $40 solution would suffice.
\r\n
Whenever possible, test devices for at least five minutes, focusing on pressure, heat, and noise. Choose stores that allow at-home trials with straightforward returns, especially if you have sensitive nerves or past back injuries. Over a year, the cost per use of a $150 cushion used three times weekly can drop below $1 per session, making thoughtful selection worthwhile.
1706 {} Top Places to Buy a Back Massager Nearby (Including Walmart) Best Places to Buy a Back Massager Nearby Discover the best places to buy a back massager nearby, including Walmart, pharmacies, and specialty shops, ranked by price and convenience. back massager nearby ["back massager near me", "back massager walmart", "best place to buy back massager near me", "where to buy back massager nearby"] published 2026-01-01 00:10:19.154973+00 2026-01-01 06:30:03.587483+00 igny8 \N 12928 https://massagersmart.com/shopping-guides/health-wellness/pain-relief/top-places-to-buy-a-back-massager-nearby-including-walmart/ {} [] 0 0 {} post [] {} 90 61 21 \N 426 article \N \N {"wordpress_term_ids": {"tags": [1666, 1677, 1664, 1659, 1678, 1674, 1676, 1661, 1675, 1679], "categories": [1680], "igny8_sectors": [1671], "igny8_clusters": [1670]}} \N \N \N f \N published \N 2026-01-12 23:53:31.714746+00 +208 2026-01-01 00:08:32.503577+00Back pain can push you to buy the first massager you see, but choosing the wrong device wastes money and comfort. Understanding where to shop locally, what features matter, and how to compare options in person helps you relieve pain faster while avoiding disappointing purchases.
\r\nWhen you search for a back massager near me, results can feel overwhelming, mixing pharmacies, big-box chains, and tiny wellness boutiques. Each location stocks different massager types, from $20 handheld wands to $250 shiatsu chair pads, and policies vary widely. This guide gives you a clear roadmap so every store visit becomes focused and efficient.
\r\nInstead of scrolling endless product pages, you will learn how to use local inventory tools, read in-store labels, and test devices safely. By comparing intensity ranges, heat options, and warranties before you leave home, you can narrow choices to two or three realistic contenders. That way, you spend fifteen minutes trying devices, not ninety minutes second-guessing every box on the shelf.
\r\nWe will also look at how Walmart, pharmacies, and warehouse clubs differ in selection and pricing, including when curbside pickup or same-day delivery makes more sense than wandering aisles. By the end, you will know exactly where to buy back massager near you, which features justify higher prices, and how to avoid common in-store shopping mistakes.
\r\n\r\nShopping locally for a back massager changes the decision from guessing to experiencing. Instead of relying on star ratings, you can feel motor strength, handle weight, and cushion firmness in seconds. For many shoppers with chronic pain, trying three models side by side reveals differences that no product photo or 200-word description can capture accurately.
\r\nBuying a back massager in store means you can start using it the same day, often within an hour of purchase. When you are dealing with muscle spasms or a stiff lower back, skipping a two-to-five-day shipping window matters. Local returns also simplify problems, since you can walk into the service desk instead of printing labels and repacking boxes.
\r\nMany big-box retailers place demo units in health aisles, letting you test intensity levels, noise, and vibration patterns. Feeling how a percussion massager behaves around your shoulder blades helps you avoid models that are too aggressive. Local stores also run unadvertised clearance deals, where last season’s $80 heated cushion might drop to $39 simply because packaging changed.
\r\n"Trying a massager on your own back for just thirty seconds often reveals more than reading thirty online reviews, especially about noise and comfort." That quick test can show whether the handle angle strains your wrist or the vibration travels uncomfortably into your ribs. Local shopping turns abstract specifications into concrete sensations, guiding smarter, more confident decisions.
\r\n\r\nWalking into a store, you will usually see at least four main categories of back massagers, each designed for different pain patterns and budgets. Understanding these types before you go helps you ignore shelves that do not match your needs. It also lets you ask staff specific questions, such as which shiatsu cushions fit tall users or narrow office chairs.
\r\nHandheld percussion massagers, like the HoMedics Pro Elite or Wahl Deep Tissue, use rapid pulses up to 3,000 percussions per minute. They excel on knots around the shoulders but require arm strength to reach the mid-back. Shiatsu cushions with rotating nodes, often priced between $80 and $200, better suit people wanting hands-free relief while driving or working.
\r\nHeated models typically reach 104–113°F, which loosens muscles but may feel too warm for sensitive skin. Compact cushions around 15 inches tall fit car seats, while larger 24-inch units cover the full spine at home. Battery-powered handhelds offer portability but may run only 2–3 hours, whereas corded options deliver consistent intensity for longer sessions.
\r\n"Matching device type to your daily routine matters more than chasing the highest motor power rating." If you sit eight hours at a desk, a slim vibrating cushion that straps to your chair may get thirty minutes of daily use. Meanwhile, athletes might prefer a percussion gun they can angle toward calves, glutes, and lower back after workouts.
\r\n\r\nTyping back massager nearby into a map app can flood you with pins, but refining those results saves time and fuel. Focusing on stores that actually list health appliances or personal care electronics reduces dead-end trips. Learning how to read inventory indicators and filter by in-store availability turns your phone into a precise shopping tool.
\r\nStart with Google Maps or Apple Maps and search “back massager near me,” then tap the “Shopping” or “Health & Wellness” category. Next, open individual listings and follow links to the retailer’s website, using filters like “In-store,” “Pick up today,” or “Available at this location.” These filters help you avoid driving to branches that only ship from warehouses.
\r\nSearching for a back massager Walmart option gives you access to wide selection and aggressive pricing, often between $20 and $150. Walmart’s website and app let you check stock at specific locations before leaving home. Understanding how to interpret stock labels such as “Limited stock” or “Out of stock” prevents wasted trips and frustration.
\r\nUse the Walmart app, search “back massager,” then toggle to “Pickup” or “Today” to see items available locally. You might find a $29.88 vibrating cushion, a $49.97 shiatsu pillow, and a $89.98 heated chair pad at the same store. Checking ratings and warranty details inside the app helps you prioritize which shelf items deserve in-person testing.
\r\n| Model | Type | Approx. Price (USD) | Features |
|---|---|---|---|
| HoMedics Shiatsu Elite | Chair pad | 89.98 | Shiatsu nodes, heat, 3 intensity levels, 15-minute auto-off |
| Sharper Image Power Percussion | Handheld | 69.00 | 4 attachments, 2,800 ppm, corded power, ergonomic handle |
| HoMedics Vibration Cushion | Seat cushion | 29.88 | 5 motors, vibration only, car adapter, elastic straps |
| Wahl Deep Tissue | Handheld | 49.97 | Variable speed dial, 4 heads, up to 3,350 ppm |
| Brookstone Shiatsu Pillow | Pillow | 59.88 | 4 rotating nodes, heat, compact 13-inch width |
"Checking Walmart’s app for aisle numbers and stock status turns a twenty-minute hunt into a five-minute grab-and-go." Once in store, match the SKU or model name from your phone to shelf labels, then quickly test weight and button feel. If you are unsure, ask electronics or pharmacy staff, who often know where demo units and clearance items are hidden.
\r\n\r\nBeyond Walmart, you can buy back massager locally at pharmacies, warehouse clubs, department stores, and specialty wellness shops. Each category emphasizes different features, from compact designs at CVS to premium shiatsu units at Costco. Knowing what each retailer type usually carries lets you choose the right stop instead of visiting three stores blindly.
\r\nPharmacies like Walgreens and CVS usually stock 4–8 compact models priced between $20 and $70, focusing on simple vibration cushions and small shiatsu pillows. Warehouse clubs such as Costco or Sam’s Club lean toward higher-end chair pads and massage guns, often bundled with extra attachments. Department stores like Kohl’s or Macy’s may carry branded cushions aligned with bedding and home wellness lines.
\r\n| Retailer Type | Typical Price Range | Common Types | Notable Advantage |
|---|---|---|---|
| Pharmacy (CVS) | 20–70 | Small cushions, handheld vibrators | Frequent 20–30% coupons and loyalty rewards |
| Warehouse Club (Costco) | 70–200 | Shiatsu pads, massage guns | Generous return policies, extended warranties |
| Department Store (Kohl’s) | 40–150 | Shiatsu cushions, pillows | Kohl’s Cash promotions lowering effective price |
| Specialty Wellness Shop | 100–300 | Premium shiatsu, infrared | Knowledgeable staff, in-depth demos |
| Sporting Goods Store | 60–200 | Massage guns, rollers | Athlete-focused options, recovery tools |
"Choosing retailer type based on your budget prevents sticker shock and wasted browsing time." If you want a sub-$50 cushion tonight, a nearby pharmacy is more realistic than a specialty spa store. Conversely, if you want a long-session shiatsu chair pad with heat and neck coverage, driving a few extra miles to a warehouse club can unlock better build quality and longer warranties.
\r\n\r\nOnce you are standing in front of a shelf full of boxes, comparing back massagers in-store becomes about testing comfort, control layout, and noise. Reading wattage numbers alone does not tell you whether a device will feel pleasant after ten minutes. A structured comparison method helps you quickly eliminate models that are too weak, too loud, or awkward to hold.
\r\nStart by lifting each device to assess weight; a 3-pound massager may strain wrists during longer sessions. Then power it on, cycle through all intensity levels, and listen for rattling or high-pitched buzzing. Place it gently against your shoulder or lower back over clothing for ten to fifteen seconds to gauge pressure distribution and vibration sharpness.
\r\n"Comparing three devices back-to-back within five minutes reveals subtle differences you will feel every single day." Notice how your muscles respond immediately after testing—mild warmth and relaxation are good signs, while numbness or sharp discomfort suggests the head shape or intensity is unsuitable. Use these sensations, not just packaging claims, to choose the most sustainable daily option.
1680 {} Complete Guide to Buying a Back Massager Near You Find the Best Back Massager Near You Today Learn how to find and compare a back massager near you, from Walmart to pharmacies, with pricing, features, and in-store tips. back massager near me ["back massager nearby", "back massager walmart", "buy back massager locally", "back massager in store", "where to buy back massager near me"] approved 2026-01-01 00:08:32.503799+00 2026-01-13 01:01:04.661481+00 igny8 \N \N \N {} [] 0 0 {} page [] {} 90 61 21 \N 426 cluster_hub \N \N {} \N \N \N f \N scheduled 2026-01-13 04:00:00+00 2026-01-13 01:01:04.65581+00 +\. + + +-- +-- Data for Name: igny8_content_attributes; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_attributes (id, attribute_type, name, value, external_id, external_attribute_name, source, metadata, created_at, updated_at, tenant_id, cluster_id, content_id, sector_id, site_id, task_id) FROM stdin; +\. + + +-- +-- Data for Name: igny8_content_cluster_map; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_cluster_map (id, role, source, metadata, created_at, updated_at, tenant_id, cluster_id, content_id, sector_id, site_id, task_id) FROM stdin; +\. + + +-- +-- Data for Name: igny8_content_ideas; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_ideas (id, idea_title, description, target_keywords, status, estimated_word_count, created_at, updated_at, tenant_id, keyword_cluster_id, sector_id, site_id, taxonomy_id, content_structure, content_type, disabled, delete_reason, deleted_at, deleted_by_id, is_deleted, restore_until) FROM stdin; +364 How to Choose the Best Back Massager for Your Needs {"overview": "This article provides a step-by-step guide to selecting the most suitable back massager based on individual preferences and requirements.", "outline": {"intro_focus": "Introduce the importance of finding the right back massager tailored to personal needs.", "main_sections": [{"h2_topic": "Identify Your Pain Points", "coverage": "Discuss how to assess specific areas of back pain."}, {"h2_topic": "Consider Your Lifestyle", "coverage": "Analyze how lifestyle factors influence the choice of back massager."}, {"h2_topic": "Compare Different Types", "coverage": "Compare features of vibrating, rolling, and other types of back massagers."}, {"h2_topic": "Budget Considerations", "coverage": "Outline budget ranges and how they affect choices."}, {"h2_topic": "User Reviews and Recommendations", "coverage": "Highlight the value of user feedback in the decision-making process."}, {"h2_topic": "Final Tips for Selection", "coverage": "Provide practical tips for making the final choice."}]}} best back massager, best back massager for chair, back roller massager completed 1200 2026-01-12 11:44:29.433863+00 2026-01-12 11:45:52.272298+00 90 427 61 21 \N guide_tutorial post f \N \N \N f \N +365 Back Massagers vs. Traditional Massage: Which Is Better? {"overview": "This article compares the effectiveness of back massagers with traditional massage techniques, helping readers understand the advantages of each.", "outline": {"intro_focus": "Introduce the debate between using back massagers and opting for traditional massage services.", "main_sections": [{"h2_topic": "Understanding Traditional Massage", "coverage": "Explain what traditional massage involves and its benefits."}, {"h2_topic": "How Back Massagers Work", "coverage": "Describe the mechanisms and technology behind back massagers."}, {"h2_topic": "Benefits of Back Massagers", "coverage": "List the advantages of using back massagers at home."}, {"h2_topic": "Pros and Cons of Each Method", "coverage": "Analyze the pros and cons of both back massagers and traditional massage."}, {"h2_topic": "Cost Comparison", "coverage": "Discuss the financial implications of both options."}, {"h2_topic": "User Preferences and Experiences", "coverage": "Share insights from users of both back massagers and traditional massages."}, {"h2_topic": "Conclusion: Which Is Right for You?", "coverage": "Summarize findings and guide readers in making their choice."}]}} best back massager, back vibrating massager, best back and neck massager completed 1300 2026-01-12 11:44:29.435399+00 2026-01-12 23:40:40.417344+00 90 427 61 21 \N comparison post f \N \N \N f \N +366 10 Best Back Massagers for Pain Relief in 2023 {"overview": "This listicle ranks the top ten back massagers available in 2023, providing insights into their features and benefits.", "outline": {"intro_focus": "Set the stage for the importance of selecting a high-quality back massager for effective pain relief.", "main_sections": [{"h2_topic": "Criteria for Selection", "coverage": "Explain the criteria used to evaluate and rank the best back massagers."}, {"h2_topic": "Top 10 Back Massagers", "coverage": "List and describe each of the top ten back massagers."}, {"h2_topic": "In-Depth Reviews of Each Product", "coverage": "Provide a brief review of each product highlighting its features."}, {"h2_topic": "Pros and Cons of Each Massager", "coverage": "Outline the benefits and drawbacks of each back massager."}, {"h2_topic": "User Feedback and Ratings", "coverage": "Summarize user reviews and ratings for each massager."}, {"h2_topic": "Conclusion and Buying Recommendations", "coverage": "Wrap up with final recommendations based on the rankings."}]}} best back massager, best back and neck massager, back roller massager, back vibrating massager completed 1500 2026-01-12 11:44:29.4365+00 2026-01-12 11:46:47.886012+00 90 427 61 21 \N top_listicle post f \N \N \N f \N +351 Complete Guide to Back Massagers for Back, Neck, and Shoulders {"overview": "This hub page explores the full landscape of back massagers and targeted tools for back, neck, and shoulder relief, from chair-mounted devices to manual hooks and sticks. It helps readers understand their pain points, compare formats, and choose the right solution for everyday use or chronic tension.", "outline": {"intro_focus": "Introduce the problem of upper back, neck, and shoulder tension and position back massagers and targeted tools as a practical, at-home relief ecosystem.", "main_sections": [{"h2_topic": "Understanding Back, Neck, and Shoulder Muscle Tension", "coverage": "Explain common causes of upper body muscle pain and knots, and when massagers can help versus when to seek medical advice."}, {"h2_topic": "Types of Back Massagers: From Chairs to Handheld Tools", "coverage": "Provide an overview of main back massager categories, including chair-mounted, handheld, hooks, and sticks."}, {"h2_topic": "Back Massager for Chair: Hands-Free Relief While You Sit", "coverage": "Describe what chair back massagers are, key features, use cases, and who they\\u2019re best suited for."}, {"h2_topic": "Back and Neck Massager Tools for Targeted Upper Body Relief", "coverage": "Cover devices designed specifically for the back and neck area and how they differ from general massagers."}, {"h2_topic": "Back and Shoulder Massager Options for Desk Workers and Drivers", "coverage": "Focus on tools that effectively target shoulder and upper-back tension caused by posture and repetitive strain."}, {"h2_topic": "Back Hook Massager: Self-Massage for Hard-to-Reach Knots", "coverage": "Explain what a back hook massager is, how it works, and typical scenarios where it excels."}, {"h2_topic": "Back Massager Sticks and Bars: Simple Tools, Deep Pressure", "coverage": "Introduce back massager stick tools, their benefits, and basic techniques for using them safely."}, {"h2_topic": "How to Choose the Right Back Massager for Your Needs", "coverage": "Offer decision criteria like pain location, intensity preferences, budget, and lifestyle to guide product selection."}, {"h2_topic": "Safe Use, Frequency, and Best Practices", "coverage": "Outline safety tips, recommended session lengths, and how to integrate massager use into a wellness routine."}, {"h2_topic": "Maintaining Your Back Massager and When to Upgrade", "coverage": "Discuss cleaning, storage, lifespan of different tools, and signs it\\u2019s time to replace or upgrade your device."}]}} back massager, back massager for chair, back hook massager, back and shoulder massager, back and neck massager, back massager stick completed 1500 2025-12-29 02:39:34.920925+00 2025-12-29 02:40:10.305471+00 90 424 61 21 \N cluster_hub page f \N \N \N f \N +353 Back Massager for Chair Guide: Turn Any Seat Into a Relaxation Zone {"overview": "This guide explains how back massagers for chairs work, who they benefit most, and how to choose and set one up at home, in the car, or at the office. It emphasizes ergonomic support, pain relief, and practical buying considerations.", "outline": {"intro_focus": "Frame chair back massagers as an effortless way to get ongoing relief while working, driving, or relaxing without using your hands.", "main_sections": [{"h2_topic": "What Is a Back Massager for Chair?", "coverage": "Define chair-mounted back massagers, including cushion, pad, and full-seat designs, and how they differ from handheld tools."}, {"h2_topic": "Key Features to Look For in a Chair Back Massager", "coverage": "Explain important features such as massage modes, heat, adjustable intensity, and targeted back and neck massager functions."}, {"h2_topic": "Using a Back Massager for Chair at Work", "coverage": "Describe how to set up a chair massager at a desk, posture tips, and how to use it without disrupting productivity."}, {"h2_topic": "Using a Back Massager for Chair in the Car or at Home", "coverage": "Cover car-compatible models, safety considerations while driving, and ideal setups for couches and recliners."}, {"h2_topic": "Back and Shoulder Massager Benefits for Sedentary Lifestyles", "coverage": "Discuss how chair massagers help relieve upper-back and shoulder tension for people who sit for long periods."}, {"h2_topic": "Comparing Chair Massagers to Manual Tools Like Hooks and Sticks", "coverage": "Contrast hands-free chair massagers with manual back hook massager and back massager stick options, including pros and cons."}, {"h2_topic": "Care, Maintenance, and Safety Tips for Chair Back Massagers", "coverage": "Provide advice on cleaning, storage, safe session length, and when not to use a chair massager."}, {"h2_topic": "Who Should Avoid or Limit Use of Chair Back Massagers?", "coverage": "Outline health conditions and situations where medical guidance is recommended before use."}]}} back massager for chair, back massager, back and shoulder massager, back and neck massager, back hook massager, back massager stick completed 1500 2025-12-29 02:39:34.937484+00 2025-12-29 02:41:08.571666+00 90 424 61 21 \N guide_tutorial post f \N \N \N f \N +363 Complete Guide to the Best Back Massagers for Pain Relief {"overview": "This comprehensive guide explores various types of back massagers designed to provide pain relief and relaxation, helping users choose the right device for their needs.", "outline": {"intro_focus": "Establish the importance of back massagers in managing pain and promoting relaxation.", "main_sections": [{"h2_topic": "Understanding Back Pain", "coverage": "Discuss common causes and types of back pain."}, {"h2_topic": "Benefits of Back Massagers", "coverage": "Outline how back massagers can alleviate discomfort and enhance relaxation."}, {"h2_topic": "Types of Back Massagers", "coverage": "Introduce different kinds of back massagers including vibrating and rolling types."}, {"h2_topic": "Features to Consider", "coverage": "Highlight key features to look for when choosing a back massager."}, {"h2_topic": "Best Back Massagers for Chair Use", "coverage": "List and describe back massagers specifically designed for use on chairs."}, {"h2_topic": "Top Back and Neck Massagers", "coverage": "Review popular back and neck massagers available on the market."}, {"h2_topic": "How to Use a Back Massager Effectively", "coverage": "Provide tips for maximizing the benefits of back massagers."}, {"h2_topic": "Customer Reviews and Experiences", "coverage": "Share insights and testimonials from users of various back massagers."}, {"h2_topic": "Conclusion", "coverage": "Summarize key points and encourage readers to find the right massager."}]}} best back massager, best back massager for chair, best back and neck massager, back vibrating massager, back roller massager completed 1500 2026-01-12 11:44:29.43003+00 2026-01-12 11:45:20.346759+00 90 427 61 21 \N cluster_hub page f \N \N \N f \N +352 How to Use a Back Hook Massager for Neck and Shoulder Knots {"overview": "This how-to guide walks readers through using a back hook massager to safely and effectively release tight knots in the upper back, neck, and shoulders. It focuses on body positioning, pressure control, and practical routines for home and office use.", "outline": {"intro_focus": "Highlight the frustration of unreachable knots and explain how a back hook massager offers a simple self-massage solution.", "main_sections": [{"h2_topic": "What Is a Back Hook Massager and How Does It Work?", "coverage": "Define back hook massagers, their design, and why they\\u2019re effective for trigger points in the back, neck, and shoulders."}, {"h2_topic": "Safety Basics Before You Start Self-Massage", "coverage": "Cover key precautions, contraindications, and how to recognize when pressure is too intense or unsafe."}, {"h2_topic": "Body Positioning for Targeting Back and Shoulder Knots", "coverage": "Explain optimal standing, seated, and reclining positions to reach different areas of the back and shoulders."}, {"h2_topic": "Step-by-Step: Using a Back Hook Massager on the Neck", "coverage": "Provide a clear sequence for safely working on neck tension and common neck trigger points."}, {"h2_topic": "Step-by-Step: Using a Back Hook Massager on the Upper and Mid Back", "coverage": "Detail how to locate knots along the spine and shoulder blades and apply controlled pressure with the hook."}, {"h2_topic": "Integrating a Back Hook Massager Into Your Daily Routine", "coverage": "Offer sample routines for desk workers, drivers, and athletes, including recommended frequency and duration."}, {"h2_topic": "When to Combine a Back Hook Massager With Other Tools", "coverage": "Suggest pairing the hook with a back and neck massager, stretching, or heat for enhanced relief."}]}} back hook massager, back and shoulder massager, back and neck massager, back massager, back massager stick completed 1500 2025-12-29 02:39:34.934332+00 2025-12-29 02:40:39.135618+00 90 424 61 21 \N how_to post f \N \N \N f \N +354 7 Best Back Massager Sticks and Hooks for Shoulder Relief {"overview": "This top listicle curates the best back massager sticks and hook-style tools for targeting stubborn shoulder and upper-back knots. It ranks options by use case, highlights key features, and helps readers choose the right manual tool for self-massage.", "outline": {"intro_focus": "Emphasize how simple, low-tech tools like back massager sticks and hooks can deliver deep, precise relief without electricity or batteries.", "main_sections": [{"h2_topic": "How Back Massager Sticks and Hooks Relieve Muscle Knots", "coverage": "Explain the mechanics of leverage and pressure that make sticks and back hook massagers effective for trigger points."}, {"h2_topic": "Selection Criteria: What Makes a Great Back and Shoulder Massager Tool", "coverage": "Outline evaluation criteria such as material, grip, shape, portability, and suitability for back, neck, and shoulder areas."}, {"h2_topic": "Best Overall Back Massager Stick for Everyday Use", "coverage": "Profile a top all-around stick-style massager and why it stands out for most users."}, {"h2_topic": "Best Back Hook Massager for Deep Shoulder and Neck Knots", "coverage": "Highlight a leading hook massager ideal for reaching tight spots around the shoulder blades and neck."}, {"h2_topic": "Best Compact Back and Neck Massager Stick for Travel", "coverage": "Recommend a portable option that fits in bags and works well for quick relief on the go."}, {"h2_topic": "Best Budget-Friendly Manual Back Massager Tool", "coverage": "Feature a low-cost, high-value back massager stick or hook suitable for beginners."}, {"h2_topic": "Best Back and Shoulder Massager Set for Full-Body Coverage", "coverage": "Introduce a bundle or set that combines different tools to cover back, neck, and shoulders comprehensively."}, {"h2_topic": "How to Choose Between a Stick, Hook, or Chair Back Massager", "coverage": "Help readers decide which format fits their lifestyle, pain pattern, and budget, and when to own more than one type."}]}} back massager stick, back hook massager, back and shoulder massager, back and neck massager, back massager, back massager for chair completed 1500 2025-12-29 02:39:34.940358+00 2025-12-29 02:41:44.659992+00 90 424 61 21 \N top_listicle post f \N \N \N f \N +355 Complete Guide to Back Massagers with Heat and Handheld Designs {"overview": "This hub page explains the different types of back massagers with heat, including handheld wands and massage guns, and how to choose between them. It covers benefits, key features, safety, and buying considerations, and links out to more detailed guides and comparisons.", "outline": {"intro_focus": "Establish why people are turning to heated handheld and gun-style back massagers for at-home pain relief and muscle recovery.", "main_sections": [{"h2_topic": "What Is a Back Massager with Heat?", "coverage": "Define heated back massagers, how heat therapy works, and common technologies used (vibration, percussion, infrared, etc.)."}, {"h2_topic": "Handheld vs Massage Gun: Key Design Differences", "coverage": "Explain the structural and functional differences between traditional handheld back massagers and massage gun designs."}, {"h2_topic": "Core Benefits of Heat for Back Pain and Tension", "coverage": "Cover therapeutic benefits of heat for muscle relaxation, circulation, and pain relief specifically for the back."}, {"h2_topic": "Types of Back Massagers with Heat on the Market", "coverage": "Provide an overview of main categories: handheld wands, massage guns, cushion-style, straps, and chair attachments with heat."}, {"h2_topic": "Key Features to Look For in a Heated Back Massager", "coverage": "Discuss intensity settings, heat levels, attachments, battery life, ergonomics, noise, and portability."}, {"h2_topic": "When to Choose a Back Massager Handheld Design", "coverage": "Explain use cases, body areas, and user profiles best suited to classic handheld back massagers."}, {"h2_topic": "When a Back Massager Gun Is the Better Option", "coverage": "Describe scenarios where a massage gun design excels, such as deep tissue work, athletes, and trigger point targeting."}, {"h2_topic": "Safety Tips for Using Heated and Gun-Style Massagers", "coverage": "Outline safe usage guidelines, contraindications, recommended session length, and when to consult a professional."}, {"h2_topic": "How to Match a Back Massager to Your Specific Needs", "coverage": "Guide readers through assessing pain type, sensitivity, budget, and lifestyle to narrow down choices."}, {"h2_topic": "Care, Maintenance, and Longevity of Your Back Massager", "coverage": "Explain cleaning, storage, battery care, and how to keep a heated handheld or gun massager working effectively."}]}} back massager with heat, back massager handheld, back massager gun, heated back massager, handheld back massager, massage gun for back, back massager with heat therapy, portable back massager completed 2500 2026-01-01 00:05:24.263773+00 2026-01-03 17:52:05.365068+00 90 425 61 21 \N cluster_hub page f \N \N \N f \N +359 Complete Guide to Buying a Back Massager Near You {"overview": "This hub page explains all the ways to find and buy a back massager locally, from big-box retailers like Walmart to pharmacies and specialty wellness shops. It helps readers compare in-store and nearby options, understand what to look for, and decide where to shop based on price, convenience, and product selection.", "outline": {"intro_focus": "Introduce the challenge of choosing where to buy a back massager locally and set up the guide as a roadmap to finding the best nearby options.", "main_sections": [{"h2_topic": "Why Buy a Back Massager Locally Instead of Online?", "coverage": "Explain benefits of in-store shopping such as trying products, immediate pickup, easier returns, and local deals."}, {"h2_topic": "Types of Back Massagers You\\u2019ll Find in Local Stores", "coverage": "Cover common product types (handheld, chair pads, percussion, shiatsu, heated) and where they\\u2019re most likely stocked nearby."}, {"h2_topic": "Using \\u201cBack Massager Near Me\\u201d Searches Effectively", "coverage": "Show how to use maps, store locators, and search filters to find nearby retailers that carry back massagers."}, {"h2_topic": "Finding a Back Massager at Walmart Stores", "coverage": "Detail how to check Walmart inventory, compare in-store vs online options, and locate specific back massager models at your closest Walmart."}, {"h2_topic": "Other Major Retailers That Sell Back Massagers Nearby", "coverage": "Highlight pharmacies, warehouse clubs, department stores, and specialty wellness shops that typically stock back massagers."}, {"h2_topic": "How to Compare Back Massagers In-Store", "coverage": "Explain what to look for when testing devices locally, including intensity, comfort, noise level, and controls."}, {"h2_topic": "Price, Warranty, and Return Policies at Local Retailers", "coverage": "Outline how pricing, sales, warranties, and return windows differ between Walmart and other nearby stores."}, {"h2_topic": "When to Choose Curbside Pickup or Same-Day Delivery", "coverage": "Describe hybrid options like order-online-pickup-in-store and local delivery from big retailers and why they might be preferable."}, {"h2_topic": "Safety and Hygiene Tips for Testing Back Massagers In-Store", "coverage": "Provide basic hygiene, safety, and usage guidelines for trying or immediately using a new back massager purchased locally."}, {"h2_topic": "Checklist Before You Head to a Store Near You", "coverage": "Offer a simple pre-shopping checklist to help readers choose the right store and product type before visiting."}]}} back massager near me, back massager nearby, back massager walmart, buy back massager locally, back massager in store, where to buy back massager near me completed 1500 2026-01-01 00:05:46.70728+00 2026-01-01 00:08:32.544669+00 90 426 61 21 \N cluster_hub page f \N \N \N f \N +358 7 Best Back Massagers with Heat and Massage Gun Designs {"overview": "This top listicle ranks leading back massagers that offer heat, handheld convenience, or gun-style deep tissue relief. It includes quick pros and cons, ideal user profiles, and a mini buying guide to help readers pick the right model.", "outline": {"intro_focus": "Emphasize that the market is crowded and this curated list highlights the most reliable heated handheld and gun-style back massagers for different budgets.", "main_sections": [{"h2_topic": "How We Chose the Best Back Massagers with Heat", "coverage": "Explain evaluation criteria such as heat performance, power, ergonomics, noise, and user reviews."}, {"h2_topic": "Top Pick: Best Overall Heated Back Massager", "coverage": "Profile a well-rounded device that balances heat, performance, and value for most users."}, {"h2_topic": "Best Back Massager Handheld for Everyday Use", "coverage": "Highlight a user-friendly handheld model ideal for regular home or office use."}, {"h2_topic": "Best Back Massager Gun for Deep Tissue Relief", "coverage": "Feature a massage gun that excels at powerful back muscle and trigger point work."}, {"h2_topic": "Best Budget Heated Back Massager", "coverage": "Recommend an affordable option that still offers reliable heat and basic massage functions."}, {"h2_topic": "Best Premium Back Massager with Advanced Features", "coverage": "Showcase a higher-end device with extra modes, smart features, or superior build quality."}, {"h2_topic": "Best Portable Back Massager for Travel", "coverage": "Present a compact, lightweight option suitable for travel or office use on the go."}, {"h2_topic": "How to Choose the Right Back Massager from This List", "coverage": "Provide a short decision guide mapping common needs and preferences to the featured products."}]}} best back massager with heat, back massager with heat, back massager handheld, back massager gun, massage gun for back, top heated back massagers, portable back massager completed 2200 2026-01-01 00:05:24.282566+00 2026-01-01 00:07:59.692753+00 90 425 61 21 \N top_listicle post f \N \N \N f \N +356 How to Use a Back Massager with Heat Safely and Effectively {"overview": "This how-to guide walks users through setting up and using a back massager with heat for optimal relief while avoiding common mistakes. It focuses on positioning, session timing, intensity settings, and integrating heat therapy into a broader pain management routine.", "outline": {"intro_focus": "Highlight that correct technique and safety are crucial to getting real relief from a heated back massager without causing irritation or injury.", "main_sections": [{"h2_topic": "Understand Your Back Massager with Heat Settings", "coverage": "Explain typical heat and intensity controls, modes, and attachments so readers know what each option does."}, {"h2_topic": "Preparing Your Back and Environment for a Massage Session", "coverage": "Cover pre-session steps such as clothing choice, hydration, posture, and setting up a comfortable space."}, {"h2_topic": "Proper Positioning for Upper, Mid, and Lower Back", "coverage": "Detail how to position a handheld or gun-style massager safely on different back regions, including hard-to-reach spots."}, {"h2_topic": "Recommended Session Length and Frequency", "coverage": "Provide guidelines on how long and how often to use a heated back massager to avoid overuse while maintaining benefits."}, {"h2_topic": "Adjusting Heat and Intensity for Different Needs", "coverage": "Explain how to tailor settings for muscle relaxation, chronic pain, post-workout recovery, or stress relief."}, {"h2_topic": "Safety Precautions and When to Avoid Heat", "coverage": "List conditions and situations where heated back massagers should be used with caution or avoided entirely."}, {"h2_topic": "Incorporating a Back Massager into Your Wellness Routine", "coverage": "Describe how to combine massager use with stretching, strengthening, and ergonomic habits for long-term back health."}]}} back massager with heat, heated back massager, how to use back massager, back massager handheld, massage gun for back, safe use of back massager, heat therapy for back completed 1800 2026-01-01 00:05:24.276699+00 2026-01-01 00:06:52.448293+00 90 425 61 21 \N how_to post f \N \N \N f \N +357 Back Massager Handheld vs Gun: Which Is Better for You? {"overview": "This comparison article helps readers decide between a traditional handheld back massager and a massage gun based on their pain type, lifestyle, and budget. It breaks down design, intensity, usability, and ideal use cases to support confident purchase decisions.", "outline": {"intro_focus": "Frame the choice between handheld and gun-style back massagers as a common point of confusion for buyers seeking targeted back relief.", "main_sections": [{"h2_topic": "Overview of Back Massager Handheld Designs", "coverage": "Describe typical handheld back massager features, ergonomics, and how they are generally used."}, {"h2_topic": "Overview of Back Massager Gun Designs", "coverage": "Explain what a massage gun is, its percussive mechanism, and its common applications for back muscles."}, {"h2_topic": "Intensity, Depth, and Type of Massage", "coverage": "Compare how deeply each design works into muscle tissue, and which is better for light vs deep-tissue relief."}, {"h2_topic": "Heat Options: Where Handheld and Gun Designs Differ", "coverage": "Analyze how heat is implemented (or not) in handheld back massagers versus massage guns and what that means for users."}, {"h2_topic": "Ease of Use, Reach, and Portability", "coverage": "Contrast ergonomics, weight, noise, reach for solo use, and travel-friendliness between the two categories."}, {"h2_topic": "Price, Durability, and Maintenance", "coverage": "Break down typical price ranges, build quality, battery life, and upkeep requirements for each type."}, {"h2_topic": "Which Back Massager Is Best for Your Situation?", "coverage": "Offer scenario-based recommendations for different users such as office workers, seniors, and athletes."}, {"h2_topic": "Alternatives and Hybrid Back Massager Options", "coverage": "Mention hybrid or multi-function devices and when they might be a better fit than pure handheld or gun designs."}]}} back massager handheld, back massager gun, handheld vs massage gun, massage gun for back, best back massager, back massager with heat, handheld back massager comparison completed 2000 2026-01-01 00:05:24.279581+00 2026-01-01 00:07:24.664228+00 90 425 61 21 \N comparison post f \N \N \N f \N +360 How to Find the Best Back Massager Near Me in Minutes {"overview": "This how-to article walks readers step-by-step through using their phone or computer to quickly locate and evaluate nearby stores that sell back massagers. It focuses on practical search tactics, store tools, and on-the-spot decision tips for time-pressed shoppers.", "outline": {"intro_focus": "Frame the article around needing fast relief and using smart local search tactics to quickly find a quality back massager nearby.", "main_sections": [{"h2_topic": "Define Your Needs Before You Search", "coverage": "Guide readers to clarify pain points, budget, and preferred type of back massager so their local search is more targeted."}, {"h2_topic": "Use \\u201cBack Massager Near Me\\u201d Searches the Smart Way", "coverage": "Explain how to refine local search queries, read map results, and use filters to find relevant nearby retailers."}, {"h2_topic": "Check Live Inventory at Major Retailers", "coverage": "Show how to use tools from Walmart and other big-box stores to confirm if specific back massagers are in stock locally."}, {"h2_topic": "Compare Nearby Stores by Distance, Price, and Reviews", "coverage": "Teach readers how to weigh travel time, pricing, and store ratings to decide which local shop to visit first."}, {"h2_topic": "Call Ahead to Confirm Back Massager Availability", "coverage": "Encourage quick phone calls to verify models and prices, and suggest what questions to ask store associates."}, {"h2_topic": "Decide Between In-Store Shopping, Pickup, or Local Delivery", "coverage": "Help readers choose the best fulfillment option based on urgency, mobility, and store offerings."}, {"h2_topic": "What to Do If No Back Massager Is Available Nearby", "coverage": "Offer backup strategies such as expanding the search radius, checking neighboring towns, or using ship-to-store options."}]}} back massager near me, back massager nearby, find back massager near me, local back massager store, back massager in stock near me completed 1500 2026-01-01 00:05:46.71158+00 2026-01-01 00:09:15.280942+00 90 426 61 21 \N how_to post f \N \N \N f \N +361 Back Massager Walmart Shopping Guide: In-Store and Pickup {"overview": "This guide focuses specifically on buying a back massager at Walmart, covering how to browse options, check local availability, and choose between in-store, pickup, and delivery. It\\u2019s aimed at shoppers who prefer Walmart for price and convenience and want to optimize their purchase.", "outline": {"intro_focus": "Position Walmart as a convenient, budget-friendly place to buy a back massager and preview how to get the most from shopping there.", "main_sections": [{"h2_topic": "Why Choose Walmart for a Back Massager Purchase?", "coverage": "Outline Walmart\\u2019s advantages such as pricing, selection, store coverage, and flexible pickup options."}, {"h2_topic": "Types of Back Massagers You\\u2019ll Find at Walmart", "coverage": "Describe the main categories of back massagers commonly available at Walmart and typical price ranges."}, {"h2_topic": "How to Check Back Massager Availability at Your Local Walmart", "coverage": "Walk through using the Walmart website and app to see what\\u2019s in stock at specific nearby stores."}, {"h2_topic": "Comparing In-Store Back Massager Options vs Online-Only at Walmart", "coverage": "Explain the differences between items sold in physical stores and online-only listings, and how that affects your choice."}, {"h2_topic": "Using Walmart Pickup and Same-Day Options for Back Massagers", "coverage": "Detail how to order online with free pickup, curbside pickup, or same-day delivery for faster access."}, {"h2_topic": "Understanding Walmart\\u2019s Pricing, Returns, and Protection Plans", "coverage": "Cover price matching basics, return windows, and optional protection plans for back massagers."}, {"h2_topic": "Tips for Testing and Using Your New Back Massager Safely", "coverage": "Provide basic safety, setup, and first-use tips specific to devices commonly bought at Walmart."}, {"h2_topic": "When a Different Local Retailer Might Be Better Than Walmart", "coverage": "Briefly discuss scenarios where specialty or pharmacy stores nearby might offer better fits or unique models."}]}} back massager walmart, back massager near me, walmart back massager in store, buy back massager at walmart, walmart pickup back massager, walmart back massager nearby completed 1500 2026-01-01 00:05:46.715408+00 2026-01-01 00:09:48.760759+00 90 426 61 21 \N guide_tutorial post f \N \N \N f \N +362 Top Places to Buy a Back Massager Nearby (Including Walmart) {"overview": "This listicle ranks the best types of local stores for buying a back massager, from Walmart and big-box chains to pharmacies and specialty shops. It helps readers quickly see their top nearby options and choose the right retailer based on selection, price, and convenience.", "outline": {"intro_focus": "Highlight how many local options exist for buying a back massager and promise a ranked overview of the best nearby places to shop.", "main_sections": [{"h2_topic": "How We Chose the Best Local Places to Buy a Back Massager", "coverage": "Explain criteria like availability, price, store coverage, and customer experience used to rank retailer types."}, {"h2_topic": "Walmart: Widest Coverage and Budget-Friendly Back Massagers", "coverage": "Describe why Walmart is often the first stop, what shoppers can expect, and typical pros and cons."}, {"h2_topic": "Big-Box Retailers and Warehouse Clubs Near You", "coverage": "Cover other large chains that often carry back massagers and how they compare to Walmart for deals and selection."}, {"h2_topic": "Drugstores and Pharmacies with Back Massagers in Aisle", "coverage": "Discuss local pharmacies and drugstores as convenient options for simple, grab-and-go back massagers."}, {"h2_topic": "Specialty Health and Massage Stores in Your Area", "coverage": "Explain the benefits of visiting dedicated wellness or massage shops for higher-end or niche back massagers."}, {"h2_topic": "Department Stores and Mall Kiosks Offering Back Massagers", "coverage": "Outline what shoppers can expect from department stores and mall kiosks in terms of demos and impulse buys."}, {"h2_topic": "Choosing the Best Nearby Store for Your Budget and Needs", "coverage": "Summarize how to match store type to your budget, urgency, and desired back massager features."}, {"h2_topic": "Quick Checklist Before You Head to Any Local Store", "coverage": "Provide a concise list of what to bring and questions to ask wherever you decide to shop nearby."}]}} back massager nearby, back massager near me, back massager walmart, best place to buy back massager near me, local back massager stores, where to buy back massager nearby completed 1500 2026-01-01 00:05:46.718354+00 2026-01-01 00:10:19.18664+00 90 426 61 21 \N top_listicle post f \N \N \N f \N +\. + + +-- +-- Data for Name: igny8_content_ideas_keyword_objects; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_ideas_keyword_objects (id, contentideas_id, keywords_id) FROM stdin; +\. + + +-- +-- Data for Name: igny8_content_taxonomy_map; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_taxonomy_map (id, source, metadata, created_at, updated_at, tenant_id, content_id, sector_id, site_id, taxonomy_id, task_id) FROM stdin; +\. + + +-- +-- Data for Name: igny8_content_taxonomy_relations; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_taxonomy_relations (id, updated_at, created_at, content_id, taxonomy_id) FROM stdin; +1193 2026-01-12 23:40:40.366819+00 2026-01-12 23:40:40.366797+00 217 726 +1194 2026-01-12 23:40:40.376637+00 2026-01-12 23:40:40.376625+00 217 727 +1195 2026-01-12 23:40:40.386296+00 2026-01-12 23:40:40.386274+00 217 728 +1196 2026-01-12 23:40:40.395016+00 2026-01-12 23:40:40.394997+00 217 729 +1197 2026-01-12 23:40:40.401796+00 2026-01-12 23:40:40.401787+00 217 730 +1198 2026-01-12 23:40:40.407919+00 2026-01-12 23:40:40.407908+00 217 731 +\. + + +-- +-- Data for Name: igny8_content_taxonomy_terms; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_taxonomy_terms (id, name, slug, taxonomy_type, description, external_id, external_taxonomy, sync_status, count, metadata, created_at, updated_at, tenant_id, parent_id, sector_id, site_id, delete_reason, deleted_at, deleted_by_id, is_deleted, restore_until) FROM stdin; +726 back massagers back-massagers tag \N 0 {} 2026-01-12 23:40:40.353959+00 2026-01-12 23:40:40.35397+00 90 \N 61 21 \N \N \N f \N +727 traditional massage traditional-massage tag \N 0 {} 2026-01-12 23:40:40.373457+00 2026-01-12 23:40:40.373466+00 90 \N 61 21 \N \N \N f \N +728 pain relief pain-relief tag \N 0 {} 2026-01-12 23:40:40.381966+00 2026-01-12 23:40:40.381977+00 90 \N 61 21 \N \N \N f \N +729 relaxation relaxation tag \N 0 {} 2026-01-12 23:40:40.391216+00 2026-01-12 23:40:40.391241+00 90 \N 61 21 \N \N \N f \N +730 self-care self-care tag \N 0 {} 2026-01-12 23:40:40.398688+00 2026-01-12 23:40:40.398702+00 90 \N 61 21 \N \N \N f \N +731 Health > Wellness health-wellness category \N 0 {} 2026-01-12 23:40:40.404987+00 2026-01-12 23:40:40.404994+00 90 \N 61 21 \N \N \N f \N +\. + + +-- +-- Data for Name: igny8_content_taxonomy_terms_clusters; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_content_taxonomy_terms_clusters (id, contenttaxonomy_id, clusters_id) FROM stdin; +\. + + +-- +-- Data for Name: igny8_core_auth_historicalaccount; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_core_auth_historicalaccount (id, is_deleted, deleted_at, restore_until, delete_reason, name, slug, stripe_customer_id, credits, status, payment_method, deletion_retention_days, billing_email, billing_address_line1, billing_address_line2, billing_city, billing_state, billing_postal_code, billing_country, tax_id, usage_period_start, usage_period_end, created_at, updated_at, history_id, history_date, history_change_reason, history_type, deleted_by_id, history_user_id, owner_id, plan_id, usage_ahrefs_queries) FROM stdin; +5 f \N \N \N AWS Admin aws-admin \N 369 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-12 13:57:00.253271+00 1 2025-12-15 13:18:12.847126+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 359 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-12 13:57:00.253271+00 2 2025-12-15 13:34:50.232005+00 \N ~ \N \N 3 6 0 +90 f \N \N \N Paid 2 paid-2 \N 1762 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-13 02:06:31.600048+00 3 2025-12-15 14:29:32.777204+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1762 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 20:37:01.124248+00 4 2025-12-16 20:37:01.129138+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1762 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 20:37:01.16614+00 5 2025-12-16 20:37:01.166994+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1762 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 20:37:01.173349+00 6 2025-12-16 20:37:01.173977+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1747 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-13 02:06:31.600048+00 7 2025-12-16 20:37:01.195605+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1747 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 20:39:17.400407+00 8 2025-12-16 20:39:17.4019+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1744 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 20:37:01.173349+00 9 2025-12-16 20:39:17.499047+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1744 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 21:12:05.298093+00 10 2025-12-16 21:12:05.300141+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 20:39:17.400407+00 11 2025-12-16 21:12:05.398827+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 21:37:52.203221+00 12 2025-12-16 21:37:52.20429+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 21:37:52.210319+00 13 2025-12-16 21:37:52.21133+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 21:37:52.214529+00 14 2025-12-16 21:37:52.215155+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 21:12:05.298093+00 15 2025-12-16 21:37:52.228729+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:08:12.378629+00 16 2025-12-16 22:08:12.380467+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:08:12.384161+00 17 2025-12-16 22:08:12.384708+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:08:12.387919+00 18 2025-12-16 22:08:12.388573+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1710 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 21:37:52.214529+00 19 2025-12-16 22:08:12.399169+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1710 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:10:24.655017+00 20 2025-12-16 22:10:24.655918+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1710 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:10:24.659085+00 21 2025-12-16 22:10:24.659602+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1710 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:10:24.661878+00 22 2025-12-16 22:10:24.662321+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1695 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:08:12.387919+00 23 2025-12-16 22:10:24.670834+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1695 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:12:37.838595+00 24 2025-12-16 22:12:37.839551+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1695 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:12:37.843564+00 25 2025-12-16 22:12:37.844043+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1695 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:12:37.846525+00 26 2025-12-16 22:12:37.847006+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1680 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:10:24.661878+00 27 2025-12-16 22:12:37.856585+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1680 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:32:21.547802+00 28 2025-12-16 22:32:21.548683+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1680 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:32:21.553419+00 29 2025-12-16 22:32:21.553896+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1680 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:32:21.557128+00 30 2025-12-16 22:32:21.557837+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1665 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:12:37.846525+00 31 2025-12-16 22:32:21.570306+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1665 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:36:46.572429+00 32 2025-12-16 22:36:46.573229+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1665 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:36:46.57655+00 33 2025-12-16 22:36:46.576937+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1665 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:36:46.579048+00 34 2025-12-16 22:36:46.579505+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1650 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:32:21.557128+00 35 2025-12-16 22:36:46.589241+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1650 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:39:20.609445+00 36 2025-12-16 22:39:20.610735+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1644 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:36:46.579048+00 37 2025-12-16 22:39:20.67893+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1644 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:45:17.468644+00 38 2025-12-16 22:45:17.469805+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1644 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:45:17.475364+00 39 2025-12-16 22:45:17.47591+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1644 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:45:17.478981+00 40 2025-12-16 22:45:17.479722+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1629 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:39:20.609445+00 41 2025-12-16 22:45:17.490404+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1629 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:50:08.766711+00 42 2025-12-16 22:50:08.768186+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:45:17.478981+00 43 2025-12-16 22:50:08.814336+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:02:35.430634+00 44 2025-12-16 23:02:35.431678+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:02:35.437412+00 45 2025-12-16 23:02:35.43802+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:02:35.441005+00 46 2025-12-16 23:02:35.441558+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1607 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 22:50:08.766711+00 47 2025-12-16 23:02:35.450736+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1607 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:14:16.186907+00 48 2025-12-16 23:14:16.187623+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1592 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:02:35.441005+00 49 2025-12-16 23:14:16.19712+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1592 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:31:57.203968+00 50 2025-12-16 23:31:57.205022+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1592 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:31:57.208349+00 51 2025-12-16 23:31:57.208855+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1592 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:31:57.213226+00 52 2025-12-16 23:31:57.213883+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1577 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:14:16.186907+00 53 2025-12-16 23:31:57.227724+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1577 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:36:41.837005+00 54 2025-12-16 23:36:41.838052+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1569 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:31:57.213226+00 55 2025-12-16 23:36:41.893472+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1569 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:59:06.855585+00 56 2025-12-16 23:59:06.856961+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1569 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:59:06.863314+00 57 2025-12-16 23:59:06.864365+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1569 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:59:06.868524+00 58 2025-12-16 23:59:06.869177+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1554 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:36:41.837005+00 59 2025-12-16 23:59:06.879393+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1554 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 00:05:30.850203+00 60 2025-12-17 00:05:30.85118+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1547 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-16 23:59:06.868524+00 61 2025-12-17 00:05:30.932937+00 \N ~ \N \N 119 4 0 +5 f \N \N \N AWS Admin aws-admin \N 359 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:19:28.237815+00 62 2025-12-17 00:19:28.238828+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 354 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-12 13:57:00.253271+00 63 2025-12-17 00:19:28.287918+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 354 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:34:12.584689+00 64 2025-12-17 00:34:12.585562+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 350 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:19:28.237815+00 65 2025-12-17 00:34:12.628482+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 350 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:45:05.778714+00 66 2025-12-17 00:45:05.77962+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 350 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:45:05.782849+00 67 2025-12-17 00:45:05.783344+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:34:12.584689+00 68 2025-12-17 00:45:05.790292+00 \N ~ \N \N 3 6 0 +90 f \N \N \N Paid 2 paid-2 \N 1547 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:02:24.122263+00 69 2025-12-17 01:02:24.123199+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1547 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:02:24.128751+00 70 2025-12-17 01:02:24.129483+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1547 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:02:24.133188+00 71 2025-12-17 01:02:24.133836+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1547 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:02:24.136763+00 72 2025-12-17 01:02:24.13732+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1547 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:02:24.141235+00 73 2025-12-17 01:02:24.141899+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1532 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 00:05:30.850203+00 74 2025-12-17 01:02:24.153084+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1532 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:07:42.341847+00 75 2025-12-17 01:07:42.343294+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1526 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:02:24.141235+00 76 2025-12-17 01:07:42.405925+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1526 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:13:28.672605+00 77 2025-12-17 01:13:28.673407+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1526 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:13:28.676035+00 78 2025-12-17 01:13:28.676458+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1526 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:13:28.679248+00 79 2025-12-17 01:13:28.679688+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1511 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:07:42.341847+00 80 2025-12-17 01:13:28.686956+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1511 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:22:09.196984+00 81 2025-12-17 01:22:09.197794+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1507 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:13:28.679248+00 82 2025-12-17 01:22:09.24747+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1507 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:23:18.398179+00 83 2025-12-17 01:23:18.39948+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1503 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:22:09.196984+00 84 2025-12-17 01:23:18.460581+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1503 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:25:06.292391+00 85 2025-12-17 01:25:06.293673+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1499 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:23:18.398179+00 86 2025-12-17 01:25:06.364647+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1499 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:35:44.12353+00 87 2025-12-17 01:35:44.12459+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1495 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:25:06.292391+00 88 2025-12-17 01:35:44.190623+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1495 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:37:33.306391+00 89 2025-12-17 01:37:33.307407+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1495 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:37:33.310751+00 90 2025-12-17 01:37:33.311344+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1495 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:37:33.314451+00 91 2025-12-17 01:37:33.315463+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1495 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:37:33.318738+00 92 2025-12-17 01:37:33.319289+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1480 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:35:44.12353+00 93 2025-12-17 01:37:33.330338+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1480 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:41:32.124347+00 94 2025-12-17 01:41:32.125579+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1477 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:37:33.318738+00 95 2025-12-17 01:41:32.187918+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1477 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:47:27.496281+00 96 2025-12-17 01:47:27.497223+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1471 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:41:32.124347+00 97 2025-12-17 01:47:27.56324+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1461 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:47:27.496281+00 98 2025-12-17 02:50:14.008374+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1451 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:47:27.496281+00 99 2025-12-17 03:10:35.12025+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1451 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:12:46.812071+00 100 2025-12-17 03:12:46.813359+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1451 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:12:46.817677+00 101 2025-12-17 03:12:46.818162+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1451 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:12:46.820258+00 102 2025-12-17 03:12:46.820664+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1451 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:12:46.822541+00 103 2025-12-17 03:12:46.823029+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1436 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 01:47:27.496281+00 104 2025-12-17 03:12:46.834434+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1436 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:25:38.303026+00 105 2025-12-17 03:25:38.303767+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1436 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:25:38.311204+00 106 2025-12-17 03:25:38.31272+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1436 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:25:38.317239+00 107 2025-12-17 03:25:38.317949+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1436 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:25:38.320809+00 108 2025-12-17 03:25:38.321497+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1421 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:12:46.822541+00 109 2025-12-17 03:25:38.335575+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1421 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:30:57.590892+00 110 2025-12-17 03:30:57.591879+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1421 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:30:57.597702+00 111 2025-12-17 03:30:57.598256+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1421 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:30:57.600575+00 112 2025-12-17 03:30:57.60107+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1421 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:30:57.603452+00 113 2025-12-17 03:30:57.604276+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1406 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:25:38.320809+00 114 2025-12-17 03:30:57.623282+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1406 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:15:06.630205+00 115 2025-12-17 04:15:06.631721+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1406 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:15:06.657465+00 116 2025-12-17 04:15:06.662593+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1406 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:15:06.706241+00 117 2025-12-17 04:15:06.707522+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1406 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:15:06.725943+00 118 2025-12-17 04:15:06.727644+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1391 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 03:30:57.603452+00 119 2025-12-17 04:15:06.802205+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1391 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:35:04.56149+00 120 2025-12-17 04:35:04.562697+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1391 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:35:04.569456+00 121 2025-12-17 04:35:04.570502+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1391 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:35:04.574313+00 122 2025-12-17 04:35:04.575263+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1391 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:35:04.580279+00 123 2025-12-17 04:35:04.581069+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1376 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:15:06.725943+00 124 2025-12-17 04:35:04.600298+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1376 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:44:44.118887+00 125 2025-12-17 04:44:44.119811+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1376 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:44:44.126235+00 126 2025-12-17 04:44:44.127093+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1376 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:44:44.12975+00 127 2025-12-17 04:44:44.130323+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1361 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:35:04.580279+00 128 2025-12-17 04:44:44.145131+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1361 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:54:04.600146+00 129 2025-12-17 04:54:04.60108+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1361 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:54:04.607753+00 130 2025-12-17 04:54:04.608482+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1361 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:54:04.612034+00 131 2025-12-17 04:54:04.612777+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1361 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:54:04.617369+00 132 2025-12-17 04:54:04.618087+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1346 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:44:44.12975+00 133 2025-12-17 04:54:04.637204+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1346 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:06:48.131398+00 134 2025-12-17 05:06:48.13299+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 04:54:04.617369+00 135 2025-12-17 05:06:48.240057+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.300654+00 136 2025-12-17 05:12:17.30152+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.309877+00 137 2025-12-17 05:12:17.310941+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.319945+00 138 2025-12-17 05:12:17.320634+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.327979+00 139 2025-12-17 05:12:17.329199+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.337486+00 140 2025-12-17 05:12:17.338609+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1337 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.348343+00 141 2025-12-17 05:12:17.34922+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1327 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.348343+00 142 2025-12-17 06:33:29.004571+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1327 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:36:32.726693+00 143 2025-12-17 06:36:32.72799+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1327 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:36:32.735065+00 144 2025-12-17 06:36:32.736062+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1327 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:36:32.73997+00 145 2025-12-17 06:36:32.740489+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1327 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:36:32.742509+00 146 2025-12-17 06:36:32.74293+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1312 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 05:12:17.348343+00 147 2025-12-17 06:36:32.76031+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1312 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:43:24.746159+00 148 2025-12-17 06:43:24.747333+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1303 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:36:32.742509+00 149 2025-12-17 06:43:24.836169+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1303 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:17:40.87774+00 150 2025-12-17 07:17:40.878842+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1299 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 06:43:24.746159+00 151 2025-12-17 07:17:40.941428+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1299 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:22:17.283512+00 152 2025-12-17 07:22:17.284451+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1288 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:17:40.87774+00 153 2025-12-17 07:22:17.342703+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1288 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:46:20.464427+00 154 2025-12-17 07:46:20.465629+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1288 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:46:20.472086+00 155 2025-12-17 07:46:20.472735+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1288 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:46:20.475265+00 156 2025-12-17 07:46:20.475779+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1288 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:46:20.478663+00 157 2025-12-17 07:46:20.479171+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1273 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:22:17.283512+00 158 2025-12-17 07:46:20.489331+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1273 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:48:10.222205+00 159 2025-12-17 07:48:10.223283+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1259 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:46:20.478663+00 160 2025-12-17 07:48:10.270419+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1259 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:51:55.98117+00 161 2025-12-17 07:51:55.982544+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1248 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:48:10.222205+00 162 2025-12-17 07:51:56.066479+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1248 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 08:12:55.001947+00 163 2025-12-17 08:12:55.003724+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1238 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 07:51:55.98117+00 164 2025-12-17 08:12:55.084437+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1238 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 08:42:56.218997+00 165 2025-12-17 08:42:56.22053+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1197 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 08:12:55.001947+00 166 2025-12-17 08:42:56.282901+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1187 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 08:42:56.218997+00 167 2025-12-17 09:29:19.825486+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1177 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 08:42:56.218997+00 168 2025-12-17 09:45:28.361078+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1177 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:31:29.911453+00 169 2025-12-17 10:31:29.912368+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1177 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:31:29.919648+00 170 2025-12-17 10:31:29.92021+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1177 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:31:29.922944+00 171 2025-12-17 10:31:29.923453+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1177 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:31:29.925715+00 172 2025-12-17 10:31:29.926153+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1162 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 08:42:56.218997+00 173 2025-12-17 10:31:29.944092+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1162 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:36:52.06792+00 174 2025-12-17 10:36:52.069444+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1123 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:31:29.925715+00 175 2025-12-17 10:36:52.157174+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1123 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:03:05.286319+00 176 2025-12-17 11:03:05.287398+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 10:36:52.06792+00 177 2025-12-17 11:03:05.349823+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.800281+00 178 2025-12-17 11:30:48.801192+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.803908+00 179 2025-12-17 11:30:48.804398+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.809582+00 180 2025-12-17 11:30:48.810279+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.812231+00 181 2025-12-17 11:30:48.812764+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.818174+00 182 2025-12-17 11:30:48.818687+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1096 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.820199+00 183 2025-12-17 11:30:48.820677+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1094 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:03:05.286319+00 184 2025-12-17 11:30:48.834732+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1094 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:45:55.192237+00 185 2025-12-17 11:45:55.193294+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1074 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:30:48.820199+00 186 2025-12-17 11:45:55.234798+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1074 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:17:55.598746+00 187 2025-12-17 12:17:55.600781+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 11:45:55.192237+00 188 2025-12-17 12:17:55.705169+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.943425+00 189 2025-12-17 12:51:52.944645+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.94821+00 190 2025-12-17 12:51:52.94877+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.954995+00 191 2025-12-17 12:51:52.955614+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.957843+00 192 2025-12-17 12:51:52.958557+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.96346+00 193 2025-12-17 12:51:52.964255+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1056 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.966264+00 194 2025-12-17 12:51:52.967268+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:17:55.598746+00 195 2025-12-17 12:51:52.98631+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.434715+00 196 2025-12-17 12:54:47.435405+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.437058+00 197 2025-12-17 12:54:47.437482+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.440577+00 198 2025-12-17 12:54:47.440934+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.442039+00 199 2025-12-17 12:54:47.442445+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.445516+00 200 2025-12-17 12:54:47.446197+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1054 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.447621+00 201 2025-12-17 12:54:47.448046+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:51:52.966264+00 202 2025-12-17 12:54:47.457678+00 \N ~ \N \N 119 4 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.2262+00 203 2025-12-17 12:56:19.227063+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.229693+00 204 2025-12-17 12:56:19.230119+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.233152+00 205 2025-12-17 12:56:19.233511+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.234673+00 206 2025-12-17 12:56:19.235063+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.237996+00 207 2025-12-17 12:56:19.238445+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.239718+00 208 2025-12-17 12:56:19.240087+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.242816+00 209 2025-12-17 12:56:19.243255+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.244348+00 210 2025-12-17 12:56:19.244711+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.247815+00 211 2025-12-17 12:56:19.248222+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 335 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.249292+00 212 2025-12-17 12:56:19.249705+00 \N ~ \N \N 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 333 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 00:45:05.782849+00 213 2025-12-17 12:56:19.269798+00 \N ~ \N \N 3 6 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.720133+00 214 2025-12-17 12:57:23.721178+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.724441+00 215 2025-12-17 12:57:23.725177+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.731497+00 216 2025-12-17 12:57:23.732204+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.733996+00 217 2025-12-17 12:57:23.734541+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.739711+00 218 2025-12-17 12:57:23.740271+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1052 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.742557+00 219 2025-12-17 12:57:23.743115+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:54:47.447621+00 220 2025-12-17 12:57:23.755736+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.151873+00 221 2025-12-17 13:02:32.153584+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.157944+00 222 2025-12-17 13:02:32.158784+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.163134+00 223 2025-12-17 13:02:32.1636+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.165195+00 224 2025-12-17 13:02:32.16564+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.169546+00 225 2025-12-17 13:02:32.169971+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1050 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.171801+00 226 2025-12-17 13:02:32.172311+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 12:57:23.742557+00 227 2025-12-17 13:02:32.191889+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.89848+00 228 2025-12-17 13:06:08.899291+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.901841+00 229 2025-12-17 13:06:08.902354+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.905882+00 230 2025-12-17 13:06:08.906271+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.907589+00 231 2025-12-17 13:06:08.908007+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.910912+00 232 2025-12-17 13:06:08.911319+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1048 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.912473+00 233 2025-12-17 13:06:08.912859+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:02:32.171801+00 234 2025-12-17 13:06:08.926935+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.91071+00 235 2025-12-17 13:09:05.91251+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.920238+00 236 2025-12-17 13:09:05.921222+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.928096+00 237 2025-12-17 13:09:05.929319+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.931713+00 238 2025-12-17 13:09:05.932562+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.937973+00 239 2025-12-17 13:09:05.938565+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.940438+00 240 2025-12-17 13:09:05.941178+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.946194+00 241 2025-12-17 13:09:05.946727+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.948796+00 242 2025-12-17 13:09:05.949331+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.955081+00 243 2025-12-17 13:09:05.956392+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1046 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.95931+00 244 2025-12-17 13:09:05.960167+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1044 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:06:08.912473+00 245 2025-12-17 13:09:05.983258+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1044 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:10:07.354575+00 246 2025-12-17 13:10:07.355635+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1044 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:10:07.358561+00 247 2025-12-17 13:10:07.359252+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1044 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:10:07.367363+00 248 2025-12-17 13:10:07.368188+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1044 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:10:07.370895+00 249 2025-12-17 13:10:07.372025+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1042 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:09:05.95931+00 250 2025-12-17 13:10:07.388875+00 \N ~ \N \N 119 4 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-17 12:56:19.249292+00 251 2025-12-23 23:59:21.11217+00 \N ~ \N \N 3 6 0 +90 f \N \N \N Paid 2 paid-2 \N 1024 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:10:07.370895+00 252 2025-12-24 00:21:32.066165+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1024 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 15:22:08.351898+00 253 2025-12-24 15:22:08.355243+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1024 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 15:22:08.371813+00 254 2025-12-24 15:22:08.372371+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1024 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 15:22:08.37528+00 255 2025-12-24 15:22:08.375754+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1024 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 15:22:08.378252+00 256 2025-12-24 15:22:08.378719+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1007 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-17 13:10:07.370895+00 257 2025-12-24 15:22:08.393761+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 989 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 15:22:08.378252+00 258 2025-12-24 15:25:31.316841+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 989 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 22:22:47.914572+00 259 2025-12-24 22:22:47.917591+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 989 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 22:22:47.93949+00 260 2025-12-24 22:22:47.939938+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 989 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 22:22:47.943546+00 261 2025-12-24 22:22:47.944286+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 989 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 22:22:47.947397+00 262 2025-12-24 22:22:47.94786+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 972 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 15:22:08.378252+00 263 2025-12-24 22:22:47.965247+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 972 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 23:40:30.925542+00 264 2025-12-24 23:40:30.926706+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 22:22:47.947397+00 265 2025-12-24 23:40:31.007318+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.020985+00 266 2025-12-25 00:14:58.023488+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.028297+00 267 2025-12-25 00:14:58.028934+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.032945+00 268 2025-12-25 00:14:58.033665+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.03518+00 269 2025-12-25 00:14:58.035852+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.040464+00 270 2025-12-25 00:14:58.041324+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 940 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.043355+00 271 2025-12-25 00:14:58.044167+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 932 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-24 23:40:30.925542+00 272 2025-12-25 00:14:58.054569+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 924 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.043355+00 273 2025-12-25 00:27:04.846159+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 924 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:32:02.442463+00 274 2025-12-25 00:32:02.444441+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:14:58.043355+00 275 2025-12-25 00:32:02.553229+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.005045+00 276 2025-12-25 00:40:49.005906+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.008872+00 277 2025-12-25 00:40:49.009909+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.014478+00 278 2025-12-25 00:40:49.014963+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.016441+00 279 2025-12-25 00:40:49.016886+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.020352+00 280 2025-12-25 00:40:49.02081+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.022571+00 281 2025-12-25 00:40:49.02307+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.026956+00 282 2025-12-25 00:40:49.027391+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 877 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.028505+00 283 2025-12-25 00:40:49.028918+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:32:02.442463+00 284 2025-12-25 00:40:49.037867+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.859978+00 285 2025-12-25 01:22:03.860775+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.863109+00 286 2025-12-25 01:22:03.863563+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.866912+00 287 2025-12-25 01:22:03.867338+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.868463+00 288 2025-12-25 01:22:03.868845+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.871946+00 289 2025-12-25 01:22:03.872346+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.873458+00 290 2025-12-25 01:22:03.873811+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.87676+00 291 2025-12-25 01:22:03.87723+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.878475+00 292 2025-12-25 01:22:03.878913+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.882289+00 293 2025-12-25 01:22:03.882686+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 867 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.883821+00 294 2025-12-25 01:22:03.884266+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 00:40:49.028505+00 295 2025-12-25 01:22:03.894929+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.635081+00 296 2025-12-25 01:39:07.635808+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.637539+00 297 2025-12-25 01:39:07.637936+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.640979+00 298 2025-12-25 01:39:07.641367+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.642565+00 299 2025-12-25 01:39:07.642985+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.646477+00 300 2025-12-25 01:39:07.646961+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 856 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.648284+00 301 2025-12-25 01:39:07.6487+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:22:03.883821+00 302 2025-12-25 01:39:07.660281+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.317172+00 303 2025-12-25 02:15:08.318172+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.321279+00 304 2025-12-25 02:15:08.321885+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.325877+00 305 2025-12-25 02:15:08.326361+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.327613+00 306 2025-12-25 02:15:08.328027+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.332663+00 307 2025-12-25 02:15:08.333159+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.334909+00 308 2025-12-25 02:15:08.335808+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.339613+00 309 2025-12-25 02:15:08.34011+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.341364+00 310 2025-12-25 02:15:08.341726+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.344857+00 311 2025-12-25 02:15:08.345251+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 848 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.347244+00 312 2025-12-25 02:15:08.347799+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 835 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 01:39:07.648284+00 313 2025-12-25 02:15:08.358423+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 831 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.347244+00 314 2025-12-27 06:30:15.383313+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 831 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 07:22:18.387924+00 315 2025-12-27 07:22:18.389172+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 831 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 07:22:18.397989+00 316 2025-12-27 07:22:18.398797+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 831 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 07:22:18.402731+00 317 2025-12-27 07:22:18.403869+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 831 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 07:22:18.408457+00 318 2025-12-27 07:22:18.409084+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 826 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-25 02:15:08.347244+00 319 2025-12-27 07:22:18.421783+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 823 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 07:22:18.408457+00 320 2025-12-27 15:30:17.34363+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 823 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 21:24:31.615271+00 321 2025-12-27 21:24:31.617732+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 823 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 21:24:31.641496+00 322 2025-12-27 21:24:31.64205+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 823 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 21:24:31.64605+00 323 2025-12-27 21:24:31.646916+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 823 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 21:24:31.6505+00 324 2025-12-27 21:24:31.651359+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 818 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 07:22:18.408457+00 325 2025-12-27 21:24:31.667698+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 818 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.777235+00 326 2025-12-27 22:31:44.778052+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 818 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.785579+00 327 2025-12-27 22:31:44.786022+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 818 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.788481+00 328 2025-12-27 22:31:44.788922+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 818 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 329 2025-12-27 22:31:44.792231+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 813 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 21:24:31.6505+00 330 2025-12-27 22:31:44.802382+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 810 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 331 2025-12-27 23:29:27.870828+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 807 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 332 2025-12-28 00:51:07.059934+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 804 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 333 2025-12-28 02:22:22.51616+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 804 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:08.894656+00 334 2025-12-28 02:23:08.895311+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 804 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:08.899486+00 335 2025-12-28 02:23:08.900234+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 804 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:08.903763+00 336 2025-12-28 02:23:08.904224+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 804 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:08.906383+00 337 2025-12-28 02:23:08.906807+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 800 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 338 2025-12-28 02:23:08.923315+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 800 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:49.675293+00 339 2025-12-28 02:23:49.675747+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 800 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:49.679068+00 340 2025-12-28 02:23:49.679589+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 800 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:49.682445+00 341 2025-12-28 02:23:49.682945+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 800 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:23:49.685102+00 342 2025-12-28 02:23:49.685617+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 796 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 343 2025-12-28 02:23:49.691043+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 796 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:24:32.020133+00 344 2025-12-28 02:24:32.020672+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 796 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:24:32.023679+00 345 2025-12-28 02:24:32.02411+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 796 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:24:32.026565+00 346 2025-12-28 02:24:32.02704+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 796 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:24:32.030107+00 347 2025-12-28 02:24:32.030823+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 792 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 348 2025-12-28 02:24:32.036322+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 792 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:25:24.5342+00 349 2025-12-28 02:25:24.535105+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 788 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 350 2025-12-28 02:25:24.599211+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 788 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:30:52.669368+00 351 2025-12-28 02:30:52.670408+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 784 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 352 2025-12-28 02:30:52.711239+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 784 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:36:37.995638+00 353 2025-12-28 02:36:37.996493+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 780 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 354 2025-12-28 02:36:38.045716+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 780 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:42:05.367611+00 355 2025-12-28 02:42:05.368235+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 776 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 356 2025-12-28 02:42:05.403309+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 776 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 357 2025-12-28 02:47:42.663649+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 772 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-27 22:31:44.791737+00 358 2025-12-28 02:47:42.697703+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 772 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:12:08.35567+00 359 2025-12-28 03:12:08.35668+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 768 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 360 2025-12-28 03:12:08.403722+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 768 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:17:43.191775+00 361 2025-12-28 03:17:43.192465+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 764 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 362 2025-12-28 03:17:43.23893+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 764 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:23:12.842938+00 363 2025-12-28 03:23:12.843717+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 760 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 364 2025-12-28 03:23:12.886374+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 760 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:28:48.615829+00 365 2025-12-28 03:28:48.616459+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 756 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 366 2025-12-28 03:28:48.657436+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 756 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:34:15.829738+00 367 2025-12-28 03:34:15.830386+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 752 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 368 2025-12-28 03:34:15.86837+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 752 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 369 2025-12-28 03:37:17.645241+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 748 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 02:47:42.662837+00 370 2025-12-28 03:37:17.68252+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 748 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:24:09.549146+00 371 2025-12-28 19:24:09.55086+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 744 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 372 2025-12-28 19:24:09.615947+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 744 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:29:34.365404+00 373 2025-12-28 19:29:34.366149+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 374 2025-12-28 19:29:34.408496+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.792206+00 375 2025-12-28 19:34:50.792978+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.794768+00 376 2025-12-28 19:34:50.795367+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.799062+00 377 2025-12-28 19:34:50.799574+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.801112+00 378 2025-12-28 19:34:50.801661+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.805395+00 379 2025-12-28 19:34:50.805847+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.807161+00 380 2025-12-28 19:34:50.807592+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.811301+00 381 2025-12-28 19:34:50.811791+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.81338+00 382 2025-12-28 19:34:50.814041+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.819306+00 383 2025-12-28 19:34:50.8198+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 740 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:34:50.821209+00 384 2025-12-28 19:34:50.821635+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 385 2025-12-28 19:34:50.83086+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:05.994884+00 386 2025-12-28 19:35:05.995235+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:05.996485+00 387 2025-12-28 19:35:05.99691+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.001404+00 388 2025-12-28 19:35:06.002824+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.005211+00 389 2025-12-28 19:35:06.005826+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.011163+00 390 2025-12-28 19:35:06.011831+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.013589+00 391 2025-12-28 19:35:06.014663+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.020652+00 392 2025-12-28 19:35:06.021231+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.023202+00 393 2025-12-28 19:35:06.023799+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.02735+00 394 2025-12-28 19:35:06.02779+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 738 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:06.029573+00 395 2025-12-28 19:35:06.030471+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 396 2025-12-28 19:35:06.039299+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.616105+00 397 2025-12-28 19:35:22.616618+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.618087+00 398 2025-12-28 19:35:22.618746+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.622034+00 399 2025-12-28 19:35:22.622463+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.623608+00 400 2025-12-28 19:35:22.624027+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.629088+00 401 2025-12-28 19:35:22.629589+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.631161+00 402 2025-12-28 19:35:22.631722+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.635509+00 403 2025-12-28 19:35:22.636007+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.637579+00 404 2025-12-28 19:35:22.638074+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.642678+00 405 2025-12-28 19:35:22.643166+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 736 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:22.645531+00 406 2025-12-28 19:35:22.646286+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 407 2025-12-28 19:35:22.652861+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.31737+00 408 2025-12-28 19:35:36.317863+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.320623+00 409 2025-12-28 19:35:36.321108+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.32479+00 410 2025-12-28 19:35:36.325298+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.326904+00 411 2025-12-28 19:35:36.327384+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.330853+00 412 2025-12-28 19:35:36.331328+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.332591+00 413 2025-12-28 19:35:36.333023+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.338015+00 414 2025-12-28 19:35:36.338726+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.340795+00 415 2025-12-28 19:35:36.34148+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.346176+00 416 2025-12-28 19:35:36.346711+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 734 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:36.347949+00 417 2025-12-28 19:35:36.348439+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 418 2025-12-28 19:35:36.353717+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.095872+00 419 2025-12-28 19:35:51.096452+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.098061+00 420 2025-12-28 19:35:51.098631+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.105078+00 421 2025-12-28 19:35:51.106022+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.108138+00 422 2025-12-28 19:35:51.109158+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.114705+00 423 2025-12-28 19:35:51.1153+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.116934+00 424 2025-12-28 19:35:51.117689+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.122879+00 425 2025-12-28 19:35:51.123968+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.126098+00 426 2025-12-28 19:35:51.126669+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.130633+00 427 2025-12-28 19:35:51.131102+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 732 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:35:51.132548+00 428 2025-12-28 19:35:51.132922+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 429 2025-12-28 19:35:51.137986+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.582837+00 430 2025-12-28 19:36:03.583406+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.585216+00 431 2025-12-28 19:36:03.585956+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.590177+00 432 2025-12-28 19:36:03.590662+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.592035+00 433 2025-12-28 19:36:03.592503+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.596107+00 434 2025-12-28 19:36:03.596649+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.598194+00 435 2025-12-28 19:36:03.598622+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.6031+00 436 2025-12-28 19:36:03.603585+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.60514+00 437 2025-12-28 19:36:03.605757+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.609745+00 438 2025-12-28 19:36:03.610228+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 730 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:03.611447+00 439 2025-12-28 19:36:03.611838+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 440 2025-12-28 19:36:03.61683+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.829639+00 441 2025-12-28 19:36:14.830209+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.832098+00 442 2025-12-28 19:36:14.832934+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.83772+00 443 2025-12-28 19:36:14.838207+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.839739+00 444 2025-12-28 19:36:14.840199+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.843911+00 445 2025-12-28 19:36:14.84437+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.84555+00 446 2025-12-28 19:36:14.845881+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.849169+00 447 2025-12-28 19:36:14.849639+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 728 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:14.851111+00 448 2025-12-28 19:36:14.851601+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 449 2025-12-28 19:36:14.856485+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.102723+00 450 2025-12-28 19:36:27.103307+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.105935+00 451 2025-12-28 19:36:27.106591+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.110434+00 452 2025-12-28 19:36:27.110871+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.112136+00 453 2025-12-28 19:36:27.112602+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.116556+00 454 2025-12-28 19:36:27.116981+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.118324+00 455 2025-12-28 19:36:27.11885+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.121933+00 456 2025-12-28 19:36:27.122341+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.123647+00 457 2025-12-28 19:36:27.12411+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.127138+00 458 2025-12-28 19:36:27.127595+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 727 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:27.128929+00 459 2025-12-28 19:36:27.129438+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 460 2025-12-28 19:36:27.135055+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.411038+00 461 2025-12-28 19:36:41.411703+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.413656+00 462 2025-12-28 19:36:41.414418+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.418927+00 463 2025-12-28 19:36:41.419434+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.420842+00 464 2025-12-28 19:36:41.421229+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.425761+00 465 2025-12-28 19:36:41.426418+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.428159+00 466 2025-12-28 19:36:41.428683+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.432498+00 467 2025-12-28 19:36:41.432973+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.434357+00 468 2025-12-28 19:36:41.434735+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.437923+00 469 2025-12-28 19:36:41.438304+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 725 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:41.439449+00 470 2025-12-28 19:36:41.439817+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 471 2025-12-28 19:36:41.444318+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:54.998499+00 472 2025-12-28 19:36:54.999169+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.001351+00 473 2025-12-28 19:36:55.002637+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.009481+00 474 2025-12-28 19:36:55.010598+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.01252+00 475 2025-12-28 19:36:55.013057+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.017358+00 476 2025-12-28 19:36:55.017906+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.019321+00 477 2025-12-28 19:36:55.019739+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.02366+00 478 2025-12-28 19:36:55.024138+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.025674+00 479 2025-12-28 19:36:55.026169+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.029756+00 480 2025-12-28 19:36:55.030209+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 723 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:36:55.031869+00 481 2025-12-28 19:36:55.032619+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 482 2025-12-28 19:36:55.039928+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.332716+00 483 2025-12-28 19:37:10.333146+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.334806+00 484 2025-12-28 19:37:10.335568+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.33908+00 485 2025-12-28 19:37:10.339554+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.340762+00 486 2025-12-28 19:37:10.341118+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.34411+00 487 2025-12-28 19:37:10.344557+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.345924+00 488 2025-12-28 19:37:10.346327+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.349498+00 489 2025-12-28 19:37:10.349883+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.350956+00 490 2025-12-28 19:37:10.35138+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.354978+00 491 2025-12-28 19:37:10.355393+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 721 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:10.35648+00 492 2025-12-28 19:37:10.356847+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 493 2025-12-28 19:37:10.361936+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.140003+00 494 2025-12-28 19:37:22.140554+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.143416+00 495 2025-12-28 19:37:22.144328+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.149104+00 496 2025-12-28 19:37:22.149649+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.151101+00 497 2025-12-28 19:37:22.151575+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.155273+00 498 2025-12-28 19:37:22.155765+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.157368+00 499 2025-12-28 19:37:22.15795+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.162218+00 500 2025-12-28 19:37:22.16268+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 719 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:22.165645+00 501 2025-12-28 19:37:22.166214+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 502 2025-12-28 19:37:22.170731+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.083812+00 503 2025-12-28 19:37:35.084722+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.086856+00 504 2025-12-28 19:37:35.087499+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.092163+00 505 2025-12-28 19:37:35.092741+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.094164+00 506 2025-12-28 19:37:35.094647+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.09804+00 507 2025-12-28 19:37:35.098829+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.100535+00 508 2025-12-28 19:37:35.101277+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.105675+00 509 2025-12-28 19:37:35.106127+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.107599+00 510 2025-12-28 19:37:35.108016+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.112674+00 511 2025-12-28 19:37:35.11321+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 718 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 512 2025-12-28 19:37:35.115692+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 716 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 03:37:17.644735+00 513 2025-12-28 19:37:35.120905+00 \N ~ \N \N 119 4 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.368094+00 514 2025-12-28 20:05:52.369595+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.379215+00 515 2025-12-28 20:05:52.379892+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.387127+00 516 2025-12-28 20:05:52.387956+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.394092+00 517 2025-12-28 20:05:52.394734+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.399603+00 518 2025-12-28 20:05:52.40037+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.406549+00 519 2025-12-28 20:05:52.407065+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.411827+00 520 2025-12-28 20:05:52.412355+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.417227+00 521 2025-12-28 20:05:52.417733+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.423055+00 522 2025-12-28 20:05:52.423633+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.42968+00 523 2025-12-28 20:05:52.430198+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.435962+00 524 2025-12-28 20:05:52.43652+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.441347+00 525 2025-12-28 20:05:52.441888+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.44703+00 526 2025-12-28 20:05:52.447609+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.452102+00 527 2025-12-28 20:05:52.452644+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.457795+00 528 2025-12-28 20:05:52.458379+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.467151+00 529 2025-12-28 20:05:52.46787+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.472612+00 530 2025-12-28 20:05:52.473202+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.477775+00 531 2025-12-28 20:05:52.478532+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.484222+00 532 2025-12-28 20:05:52.485311+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.492845+00 533 2025-12-28 20:05:52.49354+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.501432+00 534 2025-12-28 20:05:52.501967+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.507699+00 535 2025-12-28 20:05:52.508302+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.514881+00 536 2025-12-28 20:05:52.515432+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.520958+00 537 2025-12-28 20:05:52.52155+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.526655+00 538 2025-12-28 20:05:52.527128+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.533375+00 539 2025-12-28 20:05:52.533938+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.538567+00 540 2025-12-28 20:05:52.539057+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.543814+00 541 2025-12-28 20:05:52.544382+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.549143+00 542 2025-12-28 20:05:52.549676+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.554094+00 543 2025-12-28 20:05:52.554605+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.560091+00 544 2025-12-28 20:05:52.560651+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.565341+00 545 2025-12-28 20:05:52.565827+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.571677+00 546 2025-12-28 20:05:52.572288+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.577577+00 547 2025-12-28 20:05:52.578222+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.585104+00 548 2025-12-28 20:05:52.585924+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.592023+00 549 2025-12-28 20:05:52.592644+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.598795+00 550 2025-12-28 20:05:52.59935+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.604738+00 551 2025-12-28 20:05:52.605487+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.611215+00 552 2025-12-28 20:05:52.611846+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.618154+00 553 2025-12-28 20:05:52.61893+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.62486+00 554 2025-12-28 20:05:52.625493+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.632147+00 555 2025-12-28 20:05:52.632715+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.637748+00 556 2025-12-28 20:05:52.638314+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.642994+00 557 2025-12-28 20:05:52.64355+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.650658+00 558 2025-12-28 20:05:52.651266+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.65668+00 559 2025-12-28 20:05:52.657184+00 \N ~ \N 3 3 6 0 +5 f \N \N \N AWS Admin aws-admin \N 323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.662798+00 560 2025-12-28 20:05:52.663381+00 \N ~ \N 3 3 6 0 +90 f \N \N \N Paid 2 paid-2 \N 716 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:11:33.333302+00 561 2025-12-28 20:11:33.334138+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 712 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 562 2025-12-28 20:11:33.374866+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 712 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:16:59.757597+00 563 2025-12-28 20:16:59.758201+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 708 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 564 2025-12-28 20:16:59.801582+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 708 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:22:25.14822+00 565 2025-12-28 20:22:25.149072+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 704 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 566 2025-12-28 20:22:25.195234+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 704 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:27:53.384027+00 567 2025-12-28 20:27:53.384932+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 700 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 568 2025-12-28 20:27:53.436011+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 700 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:35:10.522893+00 569 2025-12-28 20:35:10.524014+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 696 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:27:53.384027+00 570 2025-12-28 20:35:10.575758+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 696 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:35:43.234543+00 571 2025-12-28 20:35:43.235194+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 696 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 572 2025-12-28 20:35:43.275409+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 696 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:36:33.176195+00 573 2025-12-28 20:36:33.177216+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 692 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 19:37:35.115046+00 574 2025-12-28 20:36:33.211297+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 692 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:40:54.048476+00 575 2025-12-28 20:40:54.049706+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 688 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:36:33.176195+00 576 2025-12-28 20:40:54.111556+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 688 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 577 2025-12-28 20:46:16.063923+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 684 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:36:33.176195+00 578 2025-12-28 20:46:16.302593+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 684 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:53:43.899057+00 579 2025-12-28 20:53:43.900522+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 680 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 580 2025-12-28 20:53:43.954796+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 680 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:14.746893+00 581 2025-12-28 20:54:14.747498+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 582 2025-12-28 20:54:14.778552+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.523595+00 583 2025-12-28 20:54:31.524045+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.525428+00 584 2025-12-28 20:54:31.526004+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.530576+00 585 2025-12-28 20:54:31.530996+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.532217+00 586 2025-12-28 20:54:31.532654+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.535704+00 587 2025-12-28 20:54:31.536132+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.537298+00 588 2025-12-28 20:54:31.537737+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.540509+00 589 2025-12-28 20:54:31.54087+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 676 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:31.541931+00 590 2025-12-28 20:54:31.542353+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 591 2025-12-28 20:54:31.552078+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.704412+00 592 2025-12-28 20:54:42.705099+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.707138+00 593 2025-12-28 20:54:42.707896+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.712689+00 594 2025-12-28 20:54:42.713211+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.714871+00 595 2025-12-28 20:54:42.715555+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.719868+00 596 2025-12-28 20:54:42.720303+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.721575+00 597 2025-12-28 20:54:42.722048+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.725471+00 598 2025-12-28 20:54:42.72586+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.726915+00 599 2025-12-28 20:54:42.727296+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.730489+00 600 2025-12-28 20:54:42.730873+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 674 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:42.732916+00 601 2025-12-28 20:54:42.733339+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 602 2025-12-28 20:54:42.741477+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.430187+00 603 2025-12-28 20:54:54.430606+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.431948+00 604 2025-12-28 20:54:54.432387+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.435836+00 605 2025-12-28 20:54:54.43624+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.437441+00 606 2025-12-28 20:54:54.43784+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.441531+00 607 2025-12-28 20:54:54.442127+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.44348+00 608 2025-12-28 20:54:54.443913+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.448507+00 609 2025-12-28 20:54:54.448997+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.45039+00 610 2025-12-28 20:54:54.450827+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.45399+00 611 2025-12-28 20:54:54.45444+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 672 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:54:54.455581+00 612 2025-12-28 20:54:54.455958+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 613 2025-12-28 20:54:54.463544+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.749999+00 614 2025-12-28 20:55:06.750516+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.751888+00 615 2025-12-28 20:55:06.752301+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.755889+00 616 2025-12-28 20:55:06.756415+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.757765+00 617 2025-12-28 20:55:06.758219+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.762214+00 618 2025-12-28 20:55:06.762997+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.764653+00 619 2025-12-28 20:55:06.765604+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.770288+00 620 2025-12-28 20:55:06.770857+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.772285+00 621 2025-12-28 20:55:06.772684+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.776092+00 622 2025-12-28 20:55:06.776535+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 670 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:06.778211+00 623 2025-12-28 20:55:06.779067+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 624 2025-12-28 20:55:06.787827+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.507677+00 625 2025-12-28 20:55:18.508089+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.509307+00 626 2025-12-28 20:55:18.509669+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.512381+00 627 2025-12-28 20:55:18.512763+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.513865+00 628 2025-12-28 20:55:18.514226+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.517002+00 629 2025-12-28 20:55:18.517338+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.518399+00 630 2025-12-28 20:55:18.518776+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.521296+00 631 2025-12-28 20:55:18.52163+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.522663+00 632 2025-12-28 20:55:18.522998+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.525687+00 633 2025-12-28 20:55:18.526037+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 668 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:18.527065+00 634 2025-12-28 20:55:18.527414+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 635 2025-12-28 20:55:18.532229+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.317457+00 636 2025-12-28 20:55:29.317992+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.319607+00 637 2025-12-28 20:55:29.32013+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.324471+00 638 2025-12-28 20:55:29.325043+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.326885+00 639 2025-12-28 20:55:29.327376+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.331582+00 640 2025-12-28 20:55:29.332076+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.333599+00 641 2025-12-28 20:55:29.334072+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.337999+00 642 2025-12-28 20:55:29.338547+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.340325+00 643 2025-12-28 20:55:29.341018+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.345183+00 644 2025-12-28 20:55:29.345684+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 666 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:29.347185+00 645 2025-12-28 20:55:29.347738+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 646 2025-12-28 20:55:29.355545+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.801262+00 647 2025-12-28 20:55:41.801696+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.803417+00 648 2025-12-28 20:55:41.804155+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.807927+00 649 2025-12-28 20:55:41.808287+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.809614+00 650 2025-12-28 20:55:41.810116+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.813602+00 651 2025-12-28 20:55:41.814164+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.815649+00 652 2025-12-28 20:55:41.816083+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.819455+00 653 2025-12-28 20:55:41.819822+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.821098+00 654 2025-12-28 20:55:41.821506+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.82464+00 655 2025-12-28 20:55:41.825053+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 664 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:41.826726+00 656 2025-12-28 20:55:41.827303+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 657 2025-12-28 20:55:41.833885+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.570468+00 658 2025-12-28 20:55:54.570958+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.57266+00 659 2025-12-28 20:55:54.573266+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.577798+00 660 2025-12-28 20:55:54.578307+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.580105+00 661 2025-12-28 20:55:54.580869+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.585193+00 662 2025-12-28 20:55:54.585688+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.587166+00 663 2025-12-28 20:55:54.58764+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.591173+00 664 2025-12-28 20:55:54.591589+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.592831+00 665 2025-12-28 20:55:54.593209+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.596383+00 666 2025-12-28 20:55:54.596813+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 662 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:55:54.598044+00 667 2025-12-28 20:55:54.598487+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 668 2025-12-28 20:55:54.605018+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.225395+00 669 2025-12-28 20:56:07.225811+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.227102+00 670 2025-12-28 20:56:07.227523+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.231526+00 671 2025-12-28 20:56:07.231998+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.233372+00 672 2025-12-28 20:56:07.233826+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.237145+00 673 2025-12-28 20:56:07.237621+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.23886+00 674 2025-12-28 20:56:07.239298+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.242616+00 675 2025-12-28 20:56:07.243016+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.244144+00 676 2025-12-28 20:56:07.244569+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.247695+00 677 2025-12-28 20:56:07.248092+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 660 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:07.249295+00 678 2025-12-28 20:56:07.249717+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 679 2025-12-28 20:56:07.255624+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.412267+00 680 2025-12-28 20:56:19.412783+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.414375+00 681 2025-12-28 20:56:19.414853+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.418437+00 682 2025-12-28 20:56:19.419399+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.420747+00 683 2025-12-28 20:56:19.421184+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.425115+00 684 2025-12-28 20:56:19.425581+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.426947+00 685 2025-12-28 20:56:19.427404+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.431937+00 686 2025-12-28 20:56:19.432397+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.433629+00 687 2025-12-28 20:56:19.434011+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.437823+00 688 2025-12-28 20:56:19.438439+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 658 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:19.439837+00 689 2025-12-28 20:56:19.440271+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 690 2025-12-28 20:56:19.445124+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.237335+00 691 2025-12-28 20:56:30.237914+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.239277+00 692 2025-12-28 20:56:30.239733+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.243625+00 693 2025-12-28 20:56:30.244049+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.245416+00 694 2025-12-28 20:56:30.245856+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.249019+00 695 2025-12-28 20:56:30.249458+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.251118+00 696 2025-12-28 20:56:30.251777+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.256718+00 697 2025-12-28 20:56:30.257399+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.25894+00 698 2025-12-28 20:56:30.259475+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.263186+00 699 2025-12-28 20:56:30.263679+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 656 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 700 2025-12-28 20:56:30.265424+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 654 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:46:16.062709+00 701 2025-12-28 20:56:30.272503+00 \N ~ \N \N 119 4 0 +89 t 2025-12-28 23:30:50.570352+00 2026-01-11 23:30:50.570352+00 \N Paid 1 paid-1 \N 0 active bank_transfer 14 paid1@paid.com \N \N 2025-12-09 01:23:21.533612+00 2025-12-09 01:52:55.679624+00 702 2025-12-28 23:30:50.57476+00 \N ~ \N 3 118 4 0 +29 t 2025-12-28 23:30:50.581737+00 2026-01-11 23:30:50.581737+00 \N Home G8 home-g8 \N 8000 active stripe 14 \N \N \N 2025-12-07 08:57:12.044018+00 2025-12-07 14:11:16.788723+00 703 2025-12-28 23:30:50.583428+00 \N ~ \N 3 57 2 0 +14 t 2025-12-28 23:30:50.585228+00 2026-01-11 23:30:50.585228+00 \N Scale Account scale-account \N 8000 active bank_transfer 14 \N \N \N 2025-11-07 19:57:08.532581+00 2025-12-08 12:33:32.310169+00 704 2025-12-28 23:30:50.586332+00 \N ~ \N 3 \N 5 0 +90 f \N \N \N Paid 2 paid-2 \N 651 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 705 2025-12-29 02:39:14.132729+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 651 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:39:34.930307+00 706 2025-12-29 02:39:34.930994+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 651 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:39:34.935503+00 707 2025-12-29 02:39:34.935962+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 651 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:39:34.938491+00 708 2025-12-29 02:39:34.938961+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 651 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:39:34.941057+00 709 2025-12-29 02:39:34.941474+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 646 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 710 2025-12-29 02:39:34.950987+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 646 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:40:10.23387+00 711 2025-12-29 02:40:10.234843+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 640 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 712 2025-12-29 02:40:10.310677+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 640 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:40:39.096478+00 713 2025-12-29 02:40:39.097224+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 634 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 714 2025-12-29 02:40:39.139189+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 634 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:08.52877+00 715 2025-12-29 02:41:08.530079+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 628 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 716 2025-12-29 02:41:08.575226+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 628 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:44.630276+00 717 2025-12-29 02:41:44.631093+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 718 2025-12-29 02:41:44.663294+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.515952+00 719 2025-12-29 02:41:57.516547+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.518485+00 720 2025-12-29 02:41:57.518946+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.522346+00 721 2025-12-29 02:41:57.522768+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.523894+00 722 2025-12-29 02:41:57.52429+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.527626+00 723 2025-12-29 02:41:57.527968+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.529208+00 724 2025-12-29 02:41:57.52967+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.532873+00 725 2025-12-29 02:41:57.533236+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.534404+00 726 2025-12-29 02:41:57.534814+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.538873+00 727 2025-12-29 02:41:57.539306+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 622 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:41:57.541595+00 728 2025-12-29 02:41:57.54215+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 729 2025-12-29 02:41:57.553034+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.119503+00 730 2025-12-29 02:42:06.120168+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.122641+00 731 2025-12-29 02:42:06.123421+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.129805+00 732 2025-12-29 02:42:06.130351+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.132052+00 733 2025-12-29 02:42:06.132785+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.137277+00 734 2025-12-29 02:42:06.137767+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.139463+00 735 2025-12-29 02:42:06.140187+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.145543+00 736 2025-12-29 02:42:06.146211+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.148403+00 737 2025-12-29 02:42:06.148951+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.152837+00 738 2025-12-29 02:42:06.153375+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 620 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:06.154714+00 739 2025-12-29 02:42:06.155167+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 740 2025-12-29 02:42:06.161016+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.273239+00 741 2025-12-29 02:42:14.274012+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.275705+00 742 2025-12-29 02:42:14.276224+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.284358+00 743 2025-12-29 02:42:14.284992+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.286664+00 744 2025-12-29 02:42:14.287185+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.292448+00 745 2025-12-29 02:42:14.293335+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.295028+00 746 2025-12-29 02:42:14.295527+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.299926+00 747 2025-12-29 02:42:14.300398+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.30198+00 748 2025-12-29 02:42:14.302591+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.305731+00 749 2025-12-29 02:42:14.306161+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 618 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:14.307585+00 750 2025-12-29 02:42:14.30804+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 751 2025-12-29 02:42:14.313278+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.271761+00 752 2025-12-29 02:42:22.272492+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.274274+00 753 2025-12-29 02:42:22.274896+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.278854+00 754 2025-12-29 02:42:22.279487+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.280858+00 755 2025-12-29 02:42:22.281284+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.285058+00 756 2025-12-29 02:42:22.285516+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.28687+00 757 2025-12-29 02:42:22.287298+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.290982+00 758 2025-12-29 02:42:22.291408+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.292689+00 759 2025-12-29 02:42:22.293114+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.296109+00 760 2025-12-29 02:42:22.296471+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 616 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-29 02:42:22.297587+00 761 2025-12-29 02:42:22.297923+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 614 active local_wallet 14 paid2@paid.com 2025-12-09 02:03:02.875465+00 2026-01-08 02:03:02.875465+00 2025-12-09 02:03:03.119805+00 2025-12-28 20:56:30.264983+00 762 2025-12-29 02:42:22.302105+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 614 active bank_transfer 14 paid2@paid.com 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-30 07:18:52.561697+00 763 2025-12-30 07:18:52.564108+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 614 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 764 2025-12-31 23:55:37.664414+00 \N ~ \N 119 119 4 0 +5 f \N \N \N AWS Admin aws-admin \N 10323 active stripe 14 \N 2025-12-01 00:00:00+00 2025-12-31 23:59:59.253008+00 2025-11-07 14:23:39.426535+00 2025-12-28 20:05:52.662798+00 765 2026-01-01 00:00:00.398718+00 \N ~ \N \N 3 6 0 +90 f \N \N \N Paid 2 paid-2 \N 2614 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 766 2026-01-01 00:00:00.46048+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2611 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 767 2026-01-01 00:04:58.547893+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2611 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:24.273372+00 768 2026-01-01 00:05:24.273993+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2611 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:24.27746+00 769 2026-01-01 00:05:24.27798+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2611 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:24.280713+00 770 2026-01-01 00:05:24.281123+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2611 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:24.283305+00 771 2026-01-01 00:05:24.283748+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2606 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 772 2026-01-01 00:05:24.292201+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2606 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:46.708243+00 773 2026-01-01 00:05:46.708736+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2606 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:46.712536+00 774 2026-01-01 00:05:46.71305+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2606 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:46.716323+00 775 2026-01-01 00:05:46.716875+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2606 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:05:46.719111+00 776 2026-01-01 00:05:46.719587+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2601 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 777 2026-01-01 00:05:46.725083+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2601 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:06:25.636839+00 778 2026-01-01 00:06:25.637976+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2595 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 779 2026-01-01 00:06:25.70722+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2595 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:06:52.414407+00 780 2026-01-01 00:06:52.415032+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2589 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 781 2026-01-01 00:06:52.452503+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2589 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:07:24.632275+00 782 2026-01-01 00:07:24.632908+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2583 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 783 2026-01-01 00:07:24.66812+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2583 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:07:59.661127+00 784 2026-01-01 00:07:59.661813+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2576 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 785 2026-01-01 00:07:59.697515+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2576 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:08:32.506082+00 786 2026-01-01 00:08:32.50708+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2570 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 787 2026-01-01 00:08:32.548019+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2570 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:09:15.244526+00 788 2026-01-01 00:09:15.24509+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2564 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 789 2026-01-01 00:09:15.285502+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2564 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:09:48.720658+00 790 2026-01-01 00:09:48.721787+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2558 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 791 2026-01-01 00:09:48.765672+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2558 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:19.157297+00 792 2026-01-01 00:10:19.157955+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 793 2026-01-01 00:10:19.191016+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:32.299233+00 794 2026-01-01 00:10:32.300062+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:32.302545+00 795 2026-01-01 00:10:32.303337+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:32.308019+00 796 2026-01-01 00:10:32.30851+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:32.310079+00 797 2026-01-01 00:10:32.3107+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:32.315104+00 798 2026-01-01 00:10:32.315649+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2552 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:32.317313+00 799 2026-01-01 00:10:32.317912+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 800 2026-01-01 00:10:32.325511+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.701311+00 801 2026-01-01 00:10:39.701805+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.703175+00 802 2026-01-01 00:10:39.703655+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.70733+00 803 2026-01-01 00:10:39.707796+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.7091+00 804 2026-01-01 00:10:39.709598+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.71337+00 805 2026-01-01 00:10:39.713836+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.715141+00 806 2026-01-01 00:10:39.715654+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.719128+00 807 2026-01-01 00:10:39.719536+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.720663+00 808 2026-01-01 00:10:39.721046+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.724507+00 809 2026-01-01 00:10:39.724904+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2550 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:39.726183+00 810 2026-01-01 00:10:39.726634+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 811 2026-01-01 00:10:39.732508+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.01801+00 812 2026-01-01 00:10:47.018516+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.020495+00 813 2026-01-01 00:10:47.021522+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.025907+00 814 2026-01-01 00:10:47.026475+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.028272+00 815 2026-01-01 00:10:47.028748+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.032221+00 816 2026-01-01 00:10:47.032675+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.034156+00 817 2026-01-01 00:10:47.034621+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.038775+00 818 2026-01-01 00:10:47.039214+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.041041+00 819 2026-01-01 00:10:47.04185+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.047392+00 820 2026-01-01 00:10:47.04817+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2548 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:47.050362+00 821 2026-01-01 00:10:47.051012+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 822 2026-01-01 00:10:47.057523+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.561495+00 823 2026-01-01 00:10:56.561978+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.563757+00 824 2026-01-01 00:10:56.564293+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.568841+00 825 2026-01-01 00:10:56.569608+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.572412+00 826 2026-01-01 00:10:56.573233+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.580109+00 827 2026-01-01 00:10:56.580899+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.582856+00 828 2026-01-01 00:10:56.583374+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.589558+00 829 2026-01-01 00:10:56.590659+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.592974+00 830 2026-01-01 00:10:56.594035+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.60039+00 831 2026-01-01 00:10:56.601054+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2546 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:10:56.603684+00 832 2026-01-01 00:10:56.604244+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 833 2026-01-01 00:10:56.611914+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.888925+00 834 2026-01-01 00:11:05.889477+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.890975+00 835 2026-01-01 00:11:05.891353+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.894658+00 836 2026-01-01 00:11:05.89517+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.899286+00 837 2026-01-01 00:11:05.900431+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.908317+00 838 2026-01-01 00:11:05.908843+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.911716+00 839 2026-01-01 00:11:05.912236+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.915928+00 840 2026-01-01 00:11:05.916464+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.917802+00 841 2026-01-01 00:11:05.918209+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.926137+00 842 2026-01-01 00:11:05.926669+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2544 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:05.928667+00 843 2026-01-01 00:11:05.929337+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 844 2026-01-01 00:11:05.937263+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.43453+00 845 2026-01-01 00:11:13.43502+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.436885+00 846 2026-01-01 00:11:13.437548+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.442184+00 847 2026-01-01 00:11:13.442708+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.444227+00 848 2026-01-01 00:11:13.444666+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.448101+00 849 2026-01-01 00:11:13.448576+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.449726+00 850 2026-01-01 00:11:13.450106+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.452943+00 851 2026-01-01 00:11:13.453583+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.454883+00 852 2026-01-01 00:11:13.455308+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.45838+00 853 2026-01-01 00:11:13.45878+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2542 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:13.460322+00 854 2026-01-01 00:11:13.460816+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 855 2026-01-01 00:11:13.466908+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.299652+00 856 2026-01-01 00:11:21.300612+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.303307+00 857 2026-01-01 00:11:21.304324+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.309415+00 858 2026-01-01 00:11:21.310008+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.311833+00 859 2026-01-01 00:11:21.312311+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.316295+00 860 2026-01-01 00:11:21.316775+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.318212+00 861 2026-01-01 00:11:21.318671+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.323201+00 862 2026-01-01 00:11:21.324015+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.325732+00 863 2026-01-01 00:11:21.326237+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.330976+00 864 2026-01-01 00:11:21.331489+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2540 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:21.3333+00 865 2026-01-01 00:11:21.334049+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 866 2026-01-01 00:11:21.340854+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.11482+00 867 2026-01-01 00:11:29.115263+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.116671+00 868 2026-01-01 00:11:29.117112+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.120528+00 869 2026-01-01 00:11:29.121038+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.122332+00 870 2026-01-01 00:11:29.122781+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.125932+00 871 2026-01-01 00:11:29.126359+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.127663+00 872 2026-01-01 00:11:29.128126+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.133047+00 873 2026-01-01 00:11:29.133585+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.135169+00 874 2026-01-01 00:11:29.135636+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.13899+00 875 2026-01-01 00:11:29.139415+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2538 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 00:11:29.140528+00 876 2026-01-01 00:11:29.140876+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 2536 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2025-12-31 23:55:37.661412+00 877 2026-01-01 00:11:29.145352+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1882 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 06:10:40.203321+00 878 2026-01-01 06:10:40.205163+00 \N ~ \N 3 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1882 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:05.304318+00 879 2026-01-03 17:52:05.305694+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-01 06:10:40.203321+00 880 2026-01-03 17:52:05.37166+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.637144+00 881 2026-01-03 17:52:34.638026+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.639918+00 882 2026-01-03 17:52:34.640372+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.643825+00 883 2026-01-03 17:52:34.644243+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.645682+00 884 2026-01-03 17:52:34.646326+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.650857+00 885 2026-01-03 17:52:34.651339+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.652796+00 886 2026-01-03 17:52:34.653313+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.65763+00 887 2026-01-03 17:52:34.658122+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.659925+00 888 2026-01-03 17:52:34.660545+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.665614+00 889 2026-01-03 17:52:34.666492+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1876 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.66796+00 890 2026-01-03 17:52:34.668362+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:05.304318+00 891 2026-01-03 17:52:34.67929+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.062689+00 892 2026-01-03 18:10:26.06346+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.066207+00 893 2026-01-03 18:10:26.066864+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.070675+00 894 2026-01-03 18:10:26.071181+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.072634+00 895 2026-01-03 18:10:26.073075+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.079472+00 896 2026-01-03 18:10:26.080662+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.082272+00 897 2026-01-03 18:10:26.08304+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.088663+00 898 2026-01-03 18:10:26.089203+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.090752+00 899 2026-01-03 18:10:26.091383+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.094861+00 900 2026-01-03 18:10:26.095319+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1874 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.096644+00 901 2026-01-03 18:10:26.097847+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 17:52:34.66796+00 902 2026-01-03 18:10:26.108345+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.812197+00 903 2026-01-03 18:14:28.813011+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.815263+00 904 2026-01-03 18:14:28.816163+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.820486+00 905 2026-01-03 18:14:28.821076+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.822844+00 906 2026-01-03 18:14:28.823627+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.827938+00 907 2026-01-03 18:14:28.828454+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.830567+00 908 2026-01-03 18:14:28.831532+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.836469+00 909 2026-01-03 18:14:28.837033+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.838401+00 910 2026-01-03 18:14:28.838887+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.842429+00 911 2026-01-03 18:14:28.842856+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1872 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.844068+00 912 2026-01-03 18:14:28.844467+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1870 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:10:26.096644+00 913 2026-01-03 18:14:28.854711+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1865 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2025-12-09 02:03:02+00 2026-01-08 02:03:02+00 2025-12-09 02:03:03.119805+00 2026-01-03 18:14:28.844068+00 914 2026-01-06 00:11:36.530578+00 \N ~ \N \N 119 4 0 +104 f \N \N \N Alorig alorig \N 0 pending_payment stripe 14 bluesalman@hotmail.con PK \N \N 2026-01-07 02:26:55.485252+00 2026-01-07 02:26:55.485264+00 915 2026-01-07 02:26:55.493546+00 \N + \N \N 134 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 cus_TkYvABxJE8dumo 1000 active stripe 14 tester300us@test.com US 2026-01-07 21:00:03+00 2026-02-07 21:00:03+00 2026-01-07 20:58:01.416106+00 2026-01-08 09:00:00.283763+00 1140 2026-01-12 11:23:29.251037+00 \N ~ \N 3 \N 2 0 +104 t 2026-01-07 03:32:55.451426+00 2026-01-21 03:32:55.451426+00 \N Alorig alorig cus_TkH28nOLKNTLNE 0 pending_payment stripe 14 bluesalman@hotmail.con PK \N \N 2026-01-07 02:26:55.485252+00 2026-01-07 02:29:06.754214+00 917 2026-01-07 03:32:55.454248+00 \N ~ \N 3 134 2 0 +104 f \N \N \N Alorig alorig cus_TkH28nOLKNTLNE 0 pending_payment stripe 14 bluesalman@hotmail.con PK \N \N 2026-01-07 02:26:55.485252+00 2026-01-07 02:29:06.754214+00 916 2026-01-07 02:29:06.756687+00 \N ~ \N \N 134 2 0 +105 f \N \N \N Slaman Sadiq slaman-sadiq \N 0 pending_payment stripe 14 bluesalman@hotmail.con GB \N \N 2026-01-07 03:36:43.236836+00 2026-01-07 03:36:43.236844+00 918 2026-01-07 03:36:43.238372+00 \N + \N \N 135 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 cus_TkYvABxJE8dumo 1000 active stripe 14 tester300us@test.com US 2026-01-07 21:00:03+00 2026-02-07 21:00:03+00 2026-01-07 20:58:01.416106+00 2026-01-08 09:00:00.283763+00 1141 2026-01-12 11:23:29.383424+00 \N - \N 3 \N 2 0 +107 f \N \N \N Salman Alorig salman-alorig-54qfhi \N 0 pending_payment stripe 14 salman@alorig.com PK \N \N 2026-01-07 04:14:33.966595+00 2026-01-07 04:14:33.966601+00 920 2026-01-07 04:14:33.967511+00 \N + \N \N 137 2 0 +105 f \N \N \N Slaman Sadiq slaman-sadiq cus_TkI8Oz1cHKsolm 0 pending_payment stripe 14 bluesalman@hotmail.con GB \N \N 2026-01-07 03:36:43.236836+00 2026-01-07 03:36:43.565419+00 919 2026-01-07 03:36:43.567445+00 \N ~ \N \N 135 2 0 +107 f \N \N \N Salman Alorig salman-alorig-54qfhi cus_TkIkWm6xHNzD2g 0 pending_payment stripe 14 salman@alorig.com PK \N \N 2026-01-07 04:14:33.966595+00 2026-01-07 04:14:34.40072+00 921 2026-01-07 04:14:34.402527+00 \N ~ \N \N 137 2 0 +107 t 2026-01-07 04:17:12.153708+00 2026-01-21 04:17:12.153708+00 \N Salman Alorig salman-alorig-54qfhi cus_TkIkWm6xHNzD2g 0 pending_payment stripe 14 salman@alorig.com PK \N \N 2026-01-07 04:14:33.966595+00 2026-01-07 04:14:34.40072+00 922 2026-01-07 04:17:12.157813+00 \N ~ \N 3 \N 2 0 +105 t 2026-01-07 04:17:12.165826+00 2026-01-21 04:17:12.165826+00 \N Slaman Sadiq slaman-sadiq cus_TkI8Oz1cHKsolm 0 pending_payment stripe 14 bluesalman@hotmail.con GB \N \N 2026-01-07 03:36:43.236836+00 2026-01-07 03:36:43.565419+00 923 2026-01-07 04:17:12.167405+00 \N ~ \N 3 \N 2 0 +108 f \N \N \N Test User test-user-l03rvj \N 0 pending_payment stripe 14 test-stripe-1767759670@example.com US \N \N 2026-01-07 04:21:10.90742+00 2026-01-07 04:21:10.907428+00 924 2026-01-07 04:21:10.908289+00 \N + \N \N 138 2 0 +136 t 2026-01-12 11:24:44.360931+00 2026-01-26 11:24:44.360931+00 \N Tester 200 tester-200-fnvdza cus_TkYBQP1GWzDvgd 1000 active stripe 14 tester200@test.com PK 2026-01-07 20:11:53+00 2026-02-07 20:11:53+00 2026-01-07 20:11:27.727821+00 2026-01-07 20:24:48.155134+00 1142 2026-01-12 11:24:44.361963+00 \N ~ \N 3 167 2 0 +109 f \N \N \N tester3 tester3-0359lv \N 0 pending_payment stripe 14 test3@tester.com PK \N \N 2026-01-07 04:31:36.074074+00 2026-01-07 04:31:36.074082+00 926 2026-01-07 04:31:36.075171+00 \N + \N \N 139 4 0 +145 t 2026-01-12 11:33:48.511698+00 2026-01-26 11:33:48.511698+00 \N tacbit tacbit-5hnbn2 \N 2000 active bank_transfer 14 tacbit.com@gmail.com PK 2026-01-08 04:07:29.59793+00 2026-02-07 04:07:29.59793+00 2026-01-08 04:07:29.766724+00 2026-01-08 09:00:00.319211+00 1148 2026-01-12 11:33:48.512701+00 \N ~ \N 3 183 4 0 +109 f \N \N \N tester3 tester3-0359lv cus_TkJ1jvnrqmenMN 0 active stripe 14 test3@tester.com PK \N \N 2026-01-07 04:31:36.074074+00 2026-01-07 04:46:50.150944+00 928 2026-01-07 04:46:50.153376+00 \N ~ \N \N 139 4 0 +109 f \N \N \N tester3 tester3-0359lv cus_TkJ1jvnrqmenMN 2000 active stripe 14 test3@tester.com PK \N \N 2026-01-07 04:31:36.074074+00 2026-01-07 04:46:50.150944+00 929 2026-01-07 04:47:02.244079+00 \N ~ \N \N 139 4 0 +110 f \N \N \N Tester 4 tester-4-4nxtbq \N 0 pending_payment stripe 14 test4@tester.com PK \N \N 2026-01-07 04:51:32.663751+00 2026-01-07 04:51:32.663757+00 930 2026-01-07 04:51:32.665272+00 \N + \N \N 140 2 0 +90 f \N \N \N Paid 2 paid-2 \N 1864 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1152 2026-01-12 11:48:06.360594+00 \N ~ \N \N 119 4 0 +110 f \N \N \N Tester 4 tester-4-4nxtbq cus_TkJLbOy3RO5QIh 0 active stripe 14 test4@tester.com PK \N \N 2026-01-07 04:51:32.663751+00 2026-01-07 04:56:11.714256+00 932 2026-01-07 04:56:11.717609+00 \N ~ \N \N 140 2 0 +110 f \N \N \N Tester 4 tester-4-4nxtbq cus_TkJLbOy3RO5QIh 1000 active stripe 14 test4@tester.com PK \N \N 2026-01-07 04:51:32.663751+00 2026-01-07 04:56:11.714256+00 933 2026-01-07 04:56:11.732797+00 \N ~ \N \N 140 2 0 +111 f \N \N \N Tester 5 tester-5-8uwopx \N 0 pending_payment stripe 14 tester5@tester.com PK \N \N 2026-01-07 04:58:11.82799+00 2026-01-07 04:58:11.827999+00 934 2026-01-07 04:58:11.829406+00 \N + \N \N 141 2 0 +90 f \N \N \N Paid 2 paid-2 \N 1862 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1154 2026-01-12 11:48:32.925652+00 \N ~ \N \N 119 4 0 +111 f \N \N \N Tester 5 tester-5-8uwopx cus_TkJR4UwEp49WfY 1000 pending_payment stripe 14 tester5@tester.com PK \N \N 2026-01-07 04:58:11.82799+00 2026-01-07 04:58:12.240139+00 936 2026-01-07 04:58:47.939779+00 \N ~ \N \N 141 2 0 +111 f \N \N \N Tester 5 tester-5-8uwopx cus_TkJR4UwEp49WfY 1000 active stripe 14 tester5@tester.com PK \N \N 2026-01-07 04:58:11.82799+00 2026-01-07 04:58:47.942858+00 937 2026-01-07 04:58:47.943421+00 \N ~ \N \N 141 2 0 +109 t 2026-01-07 05:10:05.854524+00 2026-01-21 05:10:05.854524+00 Test cascade delete tester3 tester3-0359lv cus_TkJ1jvnrqmenMN 2000 active stripe 14 test3@tester.com PK \N \N 2026-01-07 04:31:36.074074+00 2026-01-07 04:46:50.150944+00 938 2026-01-07 05:10:05.859427+00 \N ~ \N \N 139 4 0 +111 f \N \N \N Tester 5 tester-5-8uwopx cus_TkJR4UwEp49WfY 0 pending_payment stripe 14 tester5@tester.com PK \N \N 2026-01-07 04:58:11.82799+00 2026-01-07 04:58:12.240139+00 935 2026-01-07 04:58:12.242034+00 \N ~ \N \N 141 2 0 +111 f \N \N \N Tester 5 tester-5-8uwopx cus_TkJR4UwEp49WfY 1000 active stripe 14 tester5@tester.com PK \N \N 2026-01-07 04:58:11.82799+00 2026-01-07 04:58:47.942858+00 939 2026-01-07 05:20:21.740205+00 \N - \N 3 141 2 0 +110 f \N \N \N Tester 4 tester-4-4nxtbq cus_TkJLbOy3RO5QIh 0 pending_payment stripe 14 test4@tester.com PK \N \N 2026-01-07 04:51:32.663751+00 2026-01-07 04:51:33.004942+00 931 2026-01-07 04:51:33.007231+00 \N ~ \N \N 140 2 0 +110 f \N \N \N Tester 4 tester-4-4nxtbq cus_TkJLbOy3RO5QIh 1000 active stripe 14 test4@tester.com PK \N \N 2026-01-07 04:51:32.663751+00 2026-01-07 04:56:11.714256+00 940 2026-01-07 05:20:21.893221+00 \N - \N 3 140 2 0 +108 f \N \N \N Test User test-user-l03rvj cus_TkIqRpowKehUQA 0 pending_payment stripe 14 test-stripe-1767759670@example.com US \N \N 2026-01-07 04:21:10.90742+00 2026-01-07 04:21:11.244124+00 925 2026-01-07 04:21:11.246164+00 \N ~ \N \N 138 2 0 +108 f \N \N \N Test User test-user-l03rvj cus_TkIqRpowKehUQA 0 pending_payment stripe 14 test-stripe-1767759670@example.com US \N \N 2026-01-07 04:21:10.90742+00 2026-01-07 04:21:11.244124+00 941 2026-01-07 05:20:21.977203+00 \N - \N 3 138 2 0 +109 f \N \N \N tester3 tester3-0359lv cus_TkJ1jvnrqmenMN 0 pending_payment stripe 14 test3@tester.com PK \N \N 2026-01-07 04:31:36.074074+00 2026-01-07 04:31:36.474509+00 927 2026-01-07 04:31:36.47628+00 \N ~ \N \N 139 4 0 +112 f \N \N \N Tester 6 tester-6-x009h0 \N 0 pending_payment stripe 14 tester6@tester6.com PK \N \N 2026-01-07 05:22:21.363416+00 2026-01-07 05:22:21.36343+00 942 2026-01-07 05:22:21.364473+00 \N + \N \N 142 2 0 +90 f \N \N \N Paid 2 paid-2 \N 1860 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1156 2026-01-12 11:48:58.61189+00 \N ~ \N \N 119 4 0 +112 f \N \N \N Tester 6 tester-6-x009h0 cus_TkJqnKi3vYJvqh 1000 pending_payment stripe 14 tester6@tester6.com PK \N \N 2026-01-07 05:22:21.363416+00 2026-01-07 05:22:21.712935+00 944 2026-01-07 05:23:03.002265+00 \N ~ \N \N 142 2 0 +112 f \N \N \N Tester 6 tester-6-x009h0 cus_TkJqnKi3vYJvqh 1000 active stripe 14 tester6@tester6.com PK \N \N 2026-01-07 05:22:21.363416+00 2026-01-07 05:23:03.005585+00 945 2026-01-07 05:23:03.00625+00 \N ~ \N \N 142 2 0 +112 f \N \N \N Tester 6 tester-6-x009h0 cus_TkJqnKi3vYJvqh 0 pending_payment stripe 14 tester6@tester6.com PK \N \N 2026-01-07 05:22:21.363416+00 2026-01-07 05:22:21.712935+00 943 2026-01-07 05:22:21.714995+00 \N ~ \N \N 142 2 0 +112 f \N \N \N Tester 6 tester-6-x009h0 cus_TkJqnKi3vYJvqh 1000 active stripe 14 tester6@tester6.com PK \N \N 2026-01-07 05:22:21.363416+00 2026-01-07 05:23:03.005585+00 946 2026-01-07 05:28:23.704087+00 \N - \N 3 142 2 0 +113 f \N \N \N Tester 7 tester-7-py4155 \N 0 pending_payment stripe 14 tester7@tester.com PK \N \N 2026-01-07 05:29:25.708433+00 2026-01-07 05:29:25.708441+00 947 2026-01-07 05:29:25.709322+00 \N + \N \N 143 2 0 +113 f \N \N \N Tester 7 tester-7-py4155 cus_TkJxYcWallZ3i8 1000 pending_payment stripe 14 tester7@tester.com PK \N \N 2026-01-07 05:29:25.708433+00 2026-01-07 05:29:26.127088+00 949 2026-01-07 05:29:52.680561+00 \N ~ \N \N 143 2 0 +113 f \N \N \N Tester 7 tester-7-py4155 cus_TkJxYcWallZ3i8 1000 active stripe 14 tester7@tester.com PK \N \N 2026-01-07 05:29:25.708433+00 2026-01-07 05:29:52.68467+00 950 2026-01-07 05:29:52.685371+00 \N ~ \N \N 143 2 0 +114 f \N \N \N Tester 8 tester-8-4l5drc \N 0 pending_payment stripe 14 tester8@tester.com PK \N \N 2026-01-07 05:49:22.821413+00 2026-01-07 05:49:22.821424+00 951 2026-01-07 05:49:22.822831+00 \N + \N \N 144 2 0 +114 f \N \N \N Tester 8 tester-8-4l5drc cus_TkKHU0KlS0IMWJ 1000 pending_payment stripe 14 tester8@tester.com PK \N \N 2026-01-07 05:49:22.821413+00 2026-01-07 05:49:23.181971+00 953 2026-01-07 05:50:28.823003+00 \N ~ \N \N 144 2 0 +114 f \N \N \N Tester 8 tester-8-4l5drc cus_TkKHU0KlS0IMWJ 1000 active stripe 14 tester8@tester.com PK \N \N 2026-01-07 05:49:22.821413+00 2026-01-07 05:50:28.826359+00 954 2026-01-07 05:50:28.827037+00 \N ~ \N \N 144 2 0 +115 f \N \N \N Tester 9 tester-9-xx4y1a \N 0 pending_payment stripe 14 tester9@tester.com PK \N \N 2026-01-07 05:55:27.240935+00 2026-01-07 05:55:27.240949+00 955 2026-01-07 05:55:27.246763+00 \N + \N \N 145 2 0 +114 f \N \N \N Tester 8 tester-8-4l5drc cus_TkKHU0KlS0IMWJ 0 pending_payment stripe 14 tester8@tester.com PK \N \N 2026-01-07 05:49:22.821413+00 2026-01-07 05:49:23.181971+00 952 2026-01-07 05:49:23.183956+00 \N ~ \N \N 144 2 0 +113 f \N \N \N Tester 7 tester-7-py4155 cus_TkJxYcWallZ3i8 0 pending_payment stripe 14 tester7@tester.com PK \N \N 2026-01-07 05:29:25.708433+00 2026-01-07 05:29:26.127088+00 948 2026-01-07 05:29:26.129484+00 \N ~ \N \N 143 2 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq cus_TkXZLOOuGtjXrJ 2000 active stripe 14 tester150@test.com PK 2026-01-07 19:34:20+00 2026-02-07 19:34:20+00 2026-01-07 19:34:04.726731+00 2026-01-07 19:46:42.267485+00 1143 2026-01-12 11:24:56.863591+00 \N ~ \N 3 \N 4 0 +115 f \N \N \N Tester 9 tester-9-xx4y1a cus_TkKNcuMoNpr8iA 1000 pending_payment stripe 14 tester9@tester.com PK \N \N 2026-01-07 05:55:27.240935+00 2026-01-07 05:55:27.637495+00 957 2026-01-07 05:56:04.514509+00 \N ~ \N \N 145 2 0 +115 f \N \N \N Tester 9 tester-9-xx4y1a cus_TkKNcuMoNpr8iA 1000 active stripe 14 tester9@tester.com PK \N \N 2026-01-07 05:55:27.240935+00 2026-01-07 05:56:04.519569+00 958 2026-01-07 05:56:04.520861+00 \N ~ \N \N 145 2 0 +115 f \N \N \N Tester 9 tester-9-xx4y1a cus_TkKNcuMoNpr8iA 0 pending_payment stripe 14 tester9@tester.com PK \N \N 2026-01-07 05:55:27.240935+00 2026-01-07 05:55:27.637495+00 956 2026-01-07 05:55:27.640053+00 \N ~ \N \N 145 2 0 +115 f \N \N \N Tester 9 tester-9-xx4y1a cus_TkKNcuMoNpr8iA 1000 active stripe 14 tester9@tester.com PK \N \N 2026-01-07 05:55:27.240935+00 2026-01-07 05:56:04.519569+00 959 2026-01-07 06:16:17.833002+00 \N - \N 3 145 2 0 +114 f \N \N \N Tester 8 tester-8-4l5drc cus_TkKHU0KlS0IMWJ 1000 active stripe 14 tester8@tester.com PK \N \N 2026-01-07 05:49:22.821413+00 2026-01-07 05:50:28.826359+00 960 2026-01-07 06:16:17.925909+00 \N - \N 3 144 2 0 +113 f \N \N \N Tester 7 tester-7-py4155 cus_TkJxYcWallZ3i8 1000 active stripe 14 tester7@tester.com PK \N \N 2026-01-07 05:29:25.708433+00 2026-01-07 05:29:52.68467+00 961 2026-01-07 06:16:18.002509+00 \N - \N 3 143 2 0 +116 f \N \N \N TEster 10 tester-10-kkl9ma \N 0 pending_payment stripe 14 tester10@test.com PK \N \N 2026-01-07 06:19:22.817542+00 2026-01-07 06:19:22.81755+00 962 2026-01-07 06:19:22.819074+00 \N + \N \N 146 2 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq cus_TkXZLOOuGtjXrJ 2000 active stripe 14 tester150@test.com PK 2026-01-07 19:34:20+00 2026-02-07 19:34:20+00 2026-01-07 19:34:04.726731+00 2026-01-07 19:46:42.267485+00 1144 2026-01-12 11:24:56.976649+00 \N - \N 3 \N 4 0 +116 f \N \N \N TEster 10 tester-10-kkl9ma cus_TkKl29oDG3roXc 1000 pending_payment stripe 14 tester10@test.com PK \N \N 2026-01-07 06:19:22.817542+00 2026-01-07 06:19:23.162394+00 964 2026-01-07 06:20:08.574993+00 \N ~ \N \N 146 2 0 +116 f \N \N \N TEster 10 tester-10-kkl9ma cus_TkKl29oDG3roXc 1000 active stripe 14 tester10@test.com PK \N \N 2026-01-07 06:19:22.817542+00 2026-01-07 06:20:08.580786+00 965 2026-01-07 06:20:08.581866+00 \N ~ \N \N 146 2 0 +117 f \N \N \N Tester 20 tester-20-itn71n \N 0 pending_payment stripe 14 tester20@test.com PK \N \N 2026-01-07 06:56:50.656596+00 2026-01-07 06:56:50.656604+00 966 2026-01-07 06:56:50.657835+00 \N + \N \N 147 2 0 +117 f \N \N \N Tester 20 tester-20-itn71n cus_TkLMEMcBsC0DzS 1000 pending_payment stripe 14 tester20@test.com PK \N \N 2026-01-07 06:56:50.656596+00 2026-01-07 06:56:51.109416+00 968 2026-01-07 06:58:02.605034+00 \N ~ \N \N 147 2 0 +117 f \N \N \N Tester 20 tester-20-itn71n cus_TkLMEMcBsC0DzS 1000 active stripe 14 tester20@test.com PK \N \N 2026-01-07 06:56:50.656596+00 2026-01-07 06:58:02.608855+00 969 2026-01-07 06:58:02.609692+00 \N ~ \N \N 147 2 0 +118 f \N \N \N Salman Sadiq salman-sadiq-idyc1o \N 0 pending_payment paypal 14 bluesalman@hotmail.com US \N \N 2026-01-07 07:59:37.24125+00 2026-01-07 07:59:37.241257+00 970 2026-01-07 07:59:37.241956+00 \N + \N \N 148 2 0 +119 f \N \N \N Tester Paypal tester-paypal-0ei84f \N 0 pending_payment paypal 14 tester30@test.com US \N \N 2026-01-07 08:14:32.404127+00 2026-01-07 08:14:32.404135+00 971 2026-01-07 08:14:32.405089+00 \N + \N \N 149 2 0 +120 f \N \N \N Tester4 Paypal tester4-paypal-963kt2 \N 0 pending_payment paypal 14 tester4paypal@test.com US \N \N 2026-01-07 08:52:36.855557+00 2026-01-07 08:52:36.855566+00 974 2026-01-07 08:52:36.856362+00 \N + \N \N 150 2 0 +117 f \N \N \N Tester 20 tester-20-itn71n cus_TkLMEMcBsC0DzS 1000 active stripe 14 tester20@test.com PK 2026-01-07 06:57:58+00 2026-02-07 06:57:58+00 2026-01-07 06:56:50.656596+00 2026-01-07 09:00:00.186391+00 975 2026-01-07 09:00:00.193933+00 \N ~ \N \N 147 2 0 +119 f \N \N \N Tester Paypal tester-paypal-0ei84f \N 5000 active paypal 14 tester30@test.com US 2026-01-07 08:31:36.333585+00 2026-02-06 08:31:36.333585+00 2026-01-07 08:14:32.404127+00 2026-01-07 09:00:00.230595+00 976 2026-01-07 09:00:00.233328+00 \N ~ \N \N 149 5 0 +116 f \N \N \N TEster 10 tester-10-kkl9ma cus_TkKl29oDG3roXc 1000 active stripe 14 tester10@test.com PK 2026-01-07 06:20:04+00 2026-02-07 06:20:04+00 2026-01-07 06:19:22.817542+00 2026-01-07 09:00:00.245392+00 977 2026-01-07 09:00:00.249311+00 \N ~ \N \N 146 2 0 +121 f \N \N \N Salman Alorig salman-alorig-kb3875 \N 0 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2026-01-07 13:15:42.243714+00 2026-01-07 13:15:42.243728+00 980 2026-01-07 13:15:42.249849+00 \N + \N \N 151 5 0 +122 f \N \N \N Tester PK tester-pk-h0pslc \N 0 pending_payment bank_transfer 14 testerpk@test.com PK \N \N 2026-01-07 13:50:58.021846+00 2026-01-07 13:50:58.021858+00 982 2026-01-07 13:50:58.023228+00 \N + \N \N 152 2 0 +122 f \N \N \N Tester PK tester-pk-h0pslc cus_TkSrD3tDCm3GJv 0 active bank_transfer 14 testerpk@test.com PK \N \N 2026-01-07 13:50:58.021846+00 2026-01-07 14:42:09.664827+00 984 2026-01-07 14:42:09.666129+00 \N ~ \N 3 152 2 0 +122 f \N \N \N Tester PK tester-pk-h0pslc cus_TkSrD3tDCm3GJv 1000 active bank_transfer 14 testerpk@test.com PK \N \N 2026-01-07 13:50:58.021846+00 2026-01-07 14:42:09.664827+00 985 2026-01-07 14:42:09.67215+00 \N ~ \N 3 152 2 0 +123 f \N \N \N Tester 10 Paypal tester-10-paypal-el1d37 \N 0 pending_payment bank_transfer 14 tester10pay@test.com PK \N \N 2026-01-07 14:48:02.754706+00 2026-01-07 14:48:02.754723+00 986 2026-01-07 14:48:02.755857+00 \N + \N \N 153 4 0 +124 f \N \N \N Tester US Stripe tester-us-stripe-68y986 \N 0 pending_payment bank_transfer 14 testerus@test.com US \N \N 2026-01-07 15:00:11.246244+00 2026-01-07 15:00:11.246256+00 989 2026-01-07 15:00:11.247848+00 \N + \N \N 154 2 0 +124 f \N \N \N Tester US Stripe tester-us-stripe-68y986 cus_TkT9bXiZh1ChOM 0 pending_payment bank_transfer 14 testerus@test.com US \N \N 2026-01-07 15:00:11.246244+00 2026-01-07 15:00:19.300575+00 990 2026-01-07 15:00:19.303414+00 \N ~ \N \N 154 2 0 +123 f \N \N \N Tester 10 Paypal tester-10-paypal-el1d37 cus_TkSxWU4VZWZThJ 0 pending_payment bank_transfer 14 tester10pay@test.com PK \N \N 2026-01-07 14:48:02.754706+00 2026-01-07 14:48:17.412642+00 987 2026-01-07 14:48:17.415034+00 \N ~ \N \N 153 4 0 +122 f \N \N \N Tester PK tester-pk-h0pslc cus_TkSrD3tDCm3GJv 0 pending_payment bank_transfer 14 testerpk@test.com PK \N \N 2026-01-07 13:50:58.021846+00 2026-01-07 14:41:39.08021+00 983 2026-01-07 14:41:39.084124+00 \N ~ \N \N 152 2 0 +121 f \N \N \N Salman Alorig salman-alorig-kb3875 cus_TkRhniOGXRNZfj 0 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2026-01-07 13:15:42.243714+00 2026-01-07 13:29:56.332069+00 981 2026-01-07 13:29:56.334663+00 \N ~ \N \N 151 5 0 +120 f \N \N \N Tester4 Paypal tester4-paypal-963kt2 cus_TkNNDCYLq5cAAn 0 pending_payment paypal 14 tester4paypal@test.com US \N \N 2026-01-07 08:52:36.855557+00 2026-01-07 09:01:46.041182+00 978 2026-01-07 09:01:46.043431+00 \N ~ \N \N 150 2 0 +120 f \N \N \N Tester4 Paypal tester4-paypal-963kt2 cus_TkNNDCYLq5cAAn 0 pending_payment paypal 14 tester4paypal@test.com US 2026-01-07 08:52:36.686377+00 2026-02-06 08:52:36.686377+00 2026-01-07 08:52:36.855557+00 2026-01-07 09:11:57.129409+00 979 2026-01-07 09:11:57.131669+00 \N ~ \N \N 150 2 0 +119 f \N \N \N Tester Paypal tester-paypal-0ei84f \N 5000 pending_payment paypal 14 tester30@test.com US \N \N 2026-01-07 08:14:32.404127+00 2026-01-07 08:14:32.404135+00 972 2026-01-07 08:31:36.353788+00 \N ~ \N \N 149 2 0 +119 f \N \N \N Tester Paypal tester-paypal-0ei84f \N 5000 active paypal 14 tester30@test.com US \N \N 2026-01-07 08:14:32.404127+00 2026-01-07 08:31:36.358595+00 973 2026-01-07 08:31:36.359684+00 \N ~ \N \N 149 5 0 +117 f \N \N \N Tester 20 tester-20-itn71n cus_TkLMEMcBsC0DzS 0 pending_payment stripe 14 tester20@test.com PK \N \N 2026-01-07 06:56:50.656596+00 2026-01-07 06:56:51.109416+00 967 2026-01-07 06:56:51.111455+00 \N ~ \N \N 147 2 0 +116 f \N \N \N TEster 10 tester-10-kkl9ma cus_TkKl29oDG3roXc 0 pending_payment stripe 14 tester10@test.com PK \N \N 2026-01-07 06:19:22.817542+00 2026-01-07 06:19:23.162394+00 963 2026-01-07 06:19:23.16455+00 \N ~ \N \N 146 2 0 +125 f \N \N \N tester5 us stripe tester5-us-stripe-lwk7sk \N 0 pending_payment bank_transfer 14 tester15us@test.com US \N \N 2026-01-07 15:08:18.486201+00 2026-01-07 15:08:18.48621+00 991 2026-01-07 15:08:18.487039+00 \N + \N \N 155 2 0 +139 t 2026-01-12 11:25:46.258875+00 2026-01-26 11:25:46.258875+00 \N Tester 500 tester-500-lv8n3t \N 5000 active paypal 14 tester500@test.com US 2026-01-07 21:47:32.328924+00 2026-02-06 21:47:32.328924+00 2026-01-07 21:41:07.958604+00 2026-01-08 09:00:00.295194+00 1145 2026-01-12 11:25:46.260001+00 \N ~ \N 3 170 5 0 +126 f \N \N \N tester 30 paypal tester-30-paypal-1u4gns \N 0 pending_payment bank_transfer 14 tester30pp@test.com US \N \N 2026-01-07 15:30:50.703164+00 2026-01-07 15:30:50.703173+00 993 2026-01-07 15:30:50.704838+00 \N + \N \N 156 2 0 +127 f \N \N \N Test 22 stripe test-22-stripe-tp6l8l \N 0 pending_payment bank_transfer 14 test22pp@test.com PK \N \N 2026-01-07 17:15:40.082963+00 2026-01-07 17:15:40.08297+00 994 2026-01-07 17:15:40.083887+00 \N + \N \N 157 2 0 +127 f \N \N \N Test 22 stripe test-22-stripe-tp6l8l cus_TkVLofIBYeaxw8 0 pending_payment bank_transfer 14 test22pp@test.com PK \N \N 2026-01-07 17:15:40.082963+00 2026-01-07 17:15:50.666183+00 995 2026-01-07 17:15:50.668087+00 \N ~ \N \N 157 2 0 +127 f \N \N \N Test 22 stripe test-22-stripe-tp6l8l cus_TkVLofIBYeaxw8 0 pending_payment bank_transfer 14 test22pp@test.com PK \N \N 2026-01-07 17:15:40.082963+00 2026-01-07 17:15:50.666183+00 996 2026-01-07 17:34:15.404028+00 \N - \N 3 157 2 0 +126 f \N \N \N tester 30 paypal tester-30-paypal-1u4gns \N 0 pending_payment bank_transfer 14 tester30pp@test.com US \N \N 2026-01-07 15:30:50.703164+00 2026-01-07 15:30:50.703173+00 997 2026-01-07 17:34:15.53504+00 \N - \N 3 156 2 0 +125 f \N \N \N tester5 us stripe tester5-us-stripe-lwk7sk cus_TkTKruCpNQZG0C 0 pending_payment bank_transfer 14 tester15us@test.com US \N \N 2026-01-07 15:08:18.486201+00 2026-01-07 15:10:49.126546+00 992 2026-01-07 15:10:49.129707+00 \N ~ \N \N 155 2 0 +125 f \N \N \N tester5 us stripe tester5-us-stripe-lwk7sk cus_TkTKruCpNQZG0C 0 pending_payment bank_transfer 14 tester15us@test.com US \N \N 2026-01-07 15:08:18.486201+00 2026-01-07 15:10:49.126546+00 998 2026-01-07 17:34:15.615312+00 \N - \N 3 155 2 0 +124 f \N \N \N Tester US Stripe tester-us-stripe-68y986 cus_TkT9bXiZh1ChOM 0 pending_payment bank_transfer 14 testerus@test.com US \N \N 2026-01-07 15:00:11.246244+00 2026-01-07 15:00:19.300575+00 999 2026-01-07 17:34:15.69738+00 \N - \N 3 154 2 0 +123 f \N \N \N Tester 10 Paypal tester-10-paypal-el1d37 cus_TkSxWU4VZWZThJ 0 pending_payment bank_transfer 14 tester10pay@test.com US \N \N 2026-01-07 14:48:02.754706+00 2026-01-07 14:49:11.784558+00 988 2026-01-07 14:49:11.78649+00 \N ~ \N \N 153 4 0 +123 f \N \N \N Tester 10 Paypal tester-10-paypal-el1d37 cus_TkSxWU4VZWZThJ 0 pending_payment bank_transfer 14 tester10pay@test.com US \N \N 2026-01-07 14:48:02.754706+00 2026-01-07 14:49:11.784558+00 1000 2026-01-07 17:34:15.782022+00 \N - \N 3 153 4 0 +122 f \N \N \N Tester PK tester-pk-h0pslc cus_TkSrD3tDCm3GJv 1000 active bank_transfer 14 testerpk@test.com PK \N \N 2026-01-07 13:50:58.021846+00 2026-01-07 14:42:09.664827+00 1001 2026-01-07 17:34:15.872153+00 \N - \N 3 152 2 0 +121 f \N \N \N Salman Alorig salman-alorig-kb3875 cus_TkRhniOGXRNZfj 0 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2026-01-07 13:15:42.243714+00 2026-01-07 13:29:56.332069+00 1002 2026-01-07 17:34:15.953147+00 \N - \N 3 151 5 0 +120 f \N \N \N Tester4 Paypal tester4-paypal-963kt2 cus_TkNNDCYLq5cAAn 0 pending_payment paypal 14 tester4paypal@test.com US 2026-01-07 08:52:36.686377+00 2026-02-06 08:52:36.686377+00 2026-01-07 08:52:36.855557+00 2026-01-07 09:11:57.129409+00 1003 2026-01-07 17:34:16.026171+00 \N - \N 3 150 2 0 +119 f \N \N \N Tester Paypal tester-paypal-0ei84f \N 5000 active paypal 14 tester30@test.com US 2026-01-07 08:31:36.333585+00 2026-02-06 08:31:36.333585+00 2026-01-07 08:14:32.404127+00 2026-01-07 09:00:00.230595+00 1004 2026-01-07 17:34:16.11134+00 \N - \N 3 149 5 0 +118 f \N \N \N Salman Sadiq salman-sadiq-idyc1o \N 0 pending_payment paypal 14 bluesalman@hotmail.com US \N \N 2026-01-07 07:59:37.24125+00 2026-01-07 07:59:37.241257+00 1005 2026-01-07 17:34:16.185468+00 \N - \N 3 148 2 0 +117 f \N \N \N Tester 20 tester-20-itn71n cus_TkLMEMcBsC0DzS 1000 active stripe 14 tester20@test.com PK 2026-01-07 06:57:58+00 2026-02-07 06:57:58+00 2026-01-07 06:56:50.656596+00 2026-01-07 09:00:00.186391+00 1006 2026-01-07 17:34:16.263296+00 \N - \N 3 147 2 0 +116 f \N \N \N TEster 10 tester-10-kkl9ma cus_TkKl29oDG3roXc 1000 active stripe 14 tester10@test.com PK 2026-01-07 06:20:04+00 2026-02-07 06:20:04+00 2026-01-07 06:19:22.817542+00 2026-01-07 09:00:00.245392+00 1007 2026-01-07 17:34:16.337015+00 \N - \N 3 146 2 0 +128 f \N \N \N tester 101 tester-101-xc00ni \N 0 pending_payment bank_transfer 14 tester101@test.com PK \N \N 2026-01-07 17:35:42.530407+00 2026-01-07 17:35:42.530419+00 1008 2026-01-07 17:35:42.534532+00 \N + \N \N 158 2 0 +129 f \N \N \N Test 10 Stripe test-10-stripe-eh8hpi \N 0 pending_payment bank_transfer 14 tester10@test.com PK \N \N 2026-01-07 18:27:16.298212+00 2026-01-07 18:27:16.298223+00 1010 2026-01-07 18:27:16.299672+00 \N + \N \N 159 2 0 +129 f \N \N \N Test 10 Stripe test-10-stripe-eh8hpi cus_TkWWkZOTv0jJm4 0 pending_payment bank_transfer 14 tester10@test.com PK \N \N 2026-01-07 18:27:16.298212+00 2026-01-07 18:28:31.824307+00 1011 2026-01-07 18:28:31.82624+00 \N ~ \N \N 159 2 0 +129 f \N \N \N Test 10 Stripe test-10-stripe-eh8hpi cus_TkWWkZOTv0jJm4 0 pending_payment bank_transfer 14 tester10@test.com PK \N \N 2026-01-07 18:27:16.298212+00 2026-01-07 18:28:31.824307+00 1012 2026-01-07 18:30:20.952045+00 \N - \N 3 159 2 0 +128 f \N \N \N tester 101 tester-101-xc00ni cus_TkVfTvyh3JXxUW 0 pending_payment bank_transfer 14 tester101@test.com PK \N \N 2026-01-07 17:35:42.530407+00 2026-01-07 17:35:51.911278+00 1009 2026-01-07 17:35:51.913487+00 \N ~ \N \N 158 2 0 +128 f \N \N \N tester 101 tester-101-xc00ni cus_TkVfTvyh3JXxUW 0 pending_payment bank_transfer 14 tester101@test.com PK \N \N 2026-01-07 17:35:42.530407+00 2026-01-07 17:35:51.911278+00 1013 2026-01-07 18:30:21.03878+00 \N - \N 3 158 2 0 +130 f \N \N \N tester 20 tester-20-x8lams \N 0 pending_payment bank_transfer 14 tester20@test.com PK \N \N 2026-01-07 18:37:42.498134+00 2026-01-07 18:37:42.49814+00 1014 2026-01-07 18:37:42.498869+00 \N + \N \N 160 2 0 +131 f \N \N \N tester 30 tester-30-8qhztb \N 0 pending_payment bank_transfer 14 tester30@test.com US \N \N 2026-01-07 18:39:24.028154+00 2026-01-07 18:39:24.028162+00 1015 2026-01-07 18:39:24.028854+00 \N + \N \N 161 2 0 +132 f \N \N \N tester 130 tester-130-lc5o3p \N 0 pending_payment bank_transfer 14 tester130@test.com PK \N \N 2026-01-07 18:49:25.293292+00 2026-01-07 18:49:25.293301+00 1017 2026-01-07 18:49:25.294104+00 \N + \N \N 162 2 0 +132 f \N \N \N tester 130 tester-130-lc5o3p cus_TkWrBA0ZGXW183 1000 pending_payment bank_transfer 14 tester130@test.com PK \N \N 2026-01-07 18:49:25.293292+00 2026-01-07 18:49:30.936934+00 1019 2026-01-07 19:14:56.204078+00 \N ~ \N \N 162 2 0 +131 f \N \N \N tester 30 tester-30-8qhztb cus_TkWif9A5vyPuJl 0 pending_payment bank_transfer 14 tester30@test.com US \N \N 2026-01-07 18:39:24.028154+00 2026-01-07 18:40:32.084447+00 1016 2026-01-07 18:40:32.086955+00 \N ~ \N \N 161 2 0 +16 t 2025-12-07 07:33:43.512962+00 2025-12-21 07:33:43.512962+00 \N newuser newuser \N 0 trial stripe 14 \N \N \N 2025-11-14 16:00:20.986958+00 2025-11-14 16:00:20.986968+00 1135 2026-01-12 10:06:29.877545+00 \N - \N \N \N 1 0 +15 t 2025-12-07 02:21:30.888437+00 2025-12-21 02:21:30.888437+00 \N test test \N 0 trial stripe 14 \N \N \N 2025-11-14 15:54:10.058772+00 2025-11-14 15:54:10.058788+00 1136 2026-01-12 10:06:29.878953+00 \N - \N \N \N 1 0 +14 t 2025-12-28 23:30:50.585228+00 2026-01-11 23:30:50.585228+00 \N Scale Account scale-account \N 8000 active bank_transfer 14 \N \N \N 2025-11-07 19:57:08.532581+00 2025-12-08 12:33:32.310169+00 1137 2026-01-12 10:06:29.87971+00 \N - \N \N \N 5 0 +13 t 2025-12-07 07:33:43.514016+00 2025-12-21 07:33:43.514016+00 \N Growth Account growth-account \N 4000 active stripe 14 \N \N \N 2025-11-07 19:57:08.296352+00 2025-11-07 19:57:08.29636+00 1138 2026-01-12 10:06:29.880465+00 \N - \N \N \N 4 0 +12 t 2025-12-07 07:33:43.51521+00 2025-12-21 07:33:43.51521+00 \N Starter Account starter-account \N 2000 active stripe 14 \N \N \N 2025-11-07 19:57:08.126334+00 2025-11-07 19:57:08.126346+00 1139 2026-01-12 10:06:29.881296+00 \N - \N \N \N 2 0 +132 f \N \N \N tester 130 tester-130-lc5o3p cus_TkWrBA0ZGXW183 1000 active bank_transfer 14 tester130@test.com PK \N \N 2026-01-07 18:49:25.293292+00 2026-01-07 19:14:56.208807+00 1020 2026-01-07 19:14:56.209515+00 \N ~ \N \N 162 2 0 +133 f \N \N \N Tester 140 tester-140-twb705 \N 0 pending_payment bank_transfer 14 tester140@test.com PK \N \N 2026-01-07 19:17:05.219572+00 2026-01-07 19:17:05.219582+00 1021 2026-01-07 19:17:05.22083+00 \N + \N \N 163 2 0 +133 f \N \N \N Tester 140 tester-140-twb705 cus_TkXIN2exB7SY4U 1000 pending_payment bank_transfer 14 tester140@test.com PK \N \N 2026-01-07 19:17:05.219572+00 2026-01-07 19:17:11.357283+00 1023 2026-01-07 19:17:23.371795+00 \N ~ \N \N 163 2 0 +133 f \N \N \N Tester 140 tester-140-twb705 cus_TkXIN2exB7SY4U 1000 active bank_transfer 14 tester140@test.com PK \N \N 2026-01-07 19:17:05.219572+00 2026-01-07 19:17:23.374215+00 1024 2026-01-07 19:17:23.374764+00 \N ~ \N \N 163 2 0 +133 f \N \N \N Tester 140 tester-140-twb705 cus_TkXIN2exB7SY4U 1000 active stripe 14 tester140@test.com PK \N \N 2026-01-07 19:17:05.219572+00 2026-01-07 19:28:12.77244+00 1025 2026-01-07 19:28:12.774534+00 \N ~ \N \N 163 2 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq \N 0 pending_payment bank_transfer 14 tester150@test.com PK \N \N 2026-01-07 19:34:04.726731+00 2026-01-07 19:34:04.726744+00 1026 2026-01-07 19:34:04.727875+00 \N + \N \N 164 4 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq cus_TkXZLOOuGtjXrJ 2000 pending_payment bank_transfer 14 tester150@test.com PK \N \N 2026-01-07 19:34:04.726731+00 2026-01-07 19:34:11.703878+00 1028 2026-01-07 19:34:31.606533+00 \N ~ \N \N 164 4 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq cus_TkXZLOOuGtjXrJ 2000 active stripe 14 tester150@test.com PK \N \N 2026-01-07 19:34:04.726731+00 2026-01-07 19:34:31.608921+00 1029 2026-01-07 19:34:31.609415+00 \N ~ \N \N 164 4 0 +135 f \N \N \N Tester 160 tester-160-n2gx70 \N 0 pending_payment bank_transfer 14 tester160@test.com PK \N \N 2026-01-07 19:51:36.598818+00 2026-01-07 19:51:36.598825+00 1031 2026-01-07 19:51:36.599478+00 \N + \N \N 166 5 0 +135 f \N \N \N Tester 160 tester-160-n2gx70 cus_TkXrynAI7a3Twm 5000 pending_payment bank_transfer 14 tester160@test.com PK \N \N 2026-01-07 19:51:36.598818+00 2026-01-07 19:51:42.902397+00 1033 2026-01-07 19:51:59.020561+00 \N ~ \N \N 166 5 0 +135 f \N \N \N Tester 160 tester-160-n2gx70 cus_TkXrynAI7a3Twm 5000 active stripe 14 tester160@test.com PK \N \N 2026-01-07 19:51:36.598818+00 2026-01-07 19:51:59.023426+00 1034 2026-01-07 19:51:59.023983+00 \N ~ \N \N 166 5 0 +136 f \N \N \N Tester 200 tester-200-fnvdza \N 0 pending_payment bank_transfer 14 tester200@test.com PK \N \N 2026-01-07 20:11:27.727821+00 2026-01-07 20:11:27.727831+00 1035 2026-01-07 20:11:27.728649+00 \N + \N \N 167 2 0 +136 f \N \N \N Tester 200 tester-200-fnvdza cus_TkYBQP1GWzDvgd 1000 pending_payment bank_transfer 14 tester200@test.com PK \N \N 2026-01-07 20:11:27.727821+00 2026-01-07 20:11:35.916067+00 1037 2026-01-07 20:11:57.969077+00 \N ~ \N \N 167 2 0 +136 f \N \N \N Tester 200 tester-200-fnvdza cus_TkYBQP1GWzDvgd 1000 active stripe 14 tester200@test.com PK \N \N 2026-01-07 20:11:27.727821+00 2026-01-07 20:11:57.971324+00 1038 2026-01-07 20:11:57.971811+00 \N ~ \N \N 167 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 \N 0 pending_payment bank_transfer 14 tester300us@test.com US \N \N 2026-01-07 20:58:01.416106+00 2026-01-07 20:58:01.416118+00 1040 2026-01-07 20:58:01.418944+00 \N + \N \N 168 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 cus_TkYvABxJE8dumo 1000 pending_payment bank_transfer 14 tester300us@test.com US \N \N 2026-01-07 20:58:01.416106+00 2026-01-07 20:58:14.495794+00 1042 2026-01-07 21:00:08.039105+00 \N ~ \N \N 168 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 cus_TkYvABxJE8dumo 1000 active stripe 14 tester300us@test.com US \N \N 2026-01-07 20:58:01.416106+00 2026-01-07 21:00:08.042278+00 1043 2026-01-07 21:00:08.042909+00 \N ~ \N \N 168 2 0 +138 f \N \N \N Tester 400 tester-400-ynzeo4 \N 0 pending_payment bank_transfer 14 tester400pp@test.com US \N \N 2026-01-07 21:15:24.572218+00 2026-01-07 21:15:24.572226+00 1044 2026-01-07 21:15:24.57298+00 \N + \N \N 169 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 cus_TkYvABxJE8dumo 0 pending_payment bank_transfer 14 tester300us@test.com US \N \N 2026-01-07 20:58:01.416106+00 2026-01-07 20:58:14.495794+00 1041 2026-01-07 20:58:14.497842+00 \N ~ \N \N 168 2 0 +136 f \N \N \N Tester 200 tester-200-fnvdza cus_TkYBQP1GWzDvgd 0 pending_payment bank_transfer 14 tester200@test.com PK \N \N 2026-01-07 20:11:27.727821+00 2026-01-07 20:11:35.916067+00 1036 2026-01-07 20:11:35.918177+00 \N ~ \N \N 167 2 0 +139 f \N \N \N Tester 500 tester-500-lv8n3t \N 0 pending_payment bank_transfer 14 tester500@test.com US \N \N 2026-01-07 21:41:07.958604+00 2026-01-07 21:41:07.958649+00 1047 2026-01-07 21:41:07.959669+00 \N + \N \N 170 5 0 +140 f \N \N \N Salman Sadiq salman-sadiq-mk1kdw \N 0 pending_payment bank_transfer 14 salman@salman-sadiq.com PK \N \N 2026-01-07 21:54:17.430781+00 2026-01-07 21:54:17.430791+00 1050 2026-01-07 21:54:17.432194+00 \N + \N \N 171 4 0 +140 f \N \N \N Salman Sadiq salman-sadiq-mk1kdw \N 0 active bank_transfer 14 salman@salman-sadiq.com PK \N \N 2026-01-07 21:54:17.430781+00 2026-01-07 22:16:42.341819+00 1051 2026-01-07 22:16:42.343312+00 \N ~ \N 3 171 4 0 +140 f \N \N \N Salman Sadiq salman-sadiq-mk1kdw \N 2000 active bank_transfer 14 salman@salman-sadiq.com PK \N \N 2026-01-07 21:54:17.430781+00 2026-01-07 22:16:42.341819+00 1052 2026-01-07 22:16:42.352383+00 \N ~ \N 3 171 4 0 +138 f \N \N \N Tester 400 tester-400-ynzeo4 \N 0 pending_payment bank_transfer 14 tester400pp@test.com US 2026-01-07 21:15:24.399423+00 2026-02-06 21:15:24.399423+00 2026-01-07 21:15:24.572218+00 2026-01-07 21:28:09.339334+00 1045 2026-01-07 21:28:09.341658+00 \N ~ \N \N 169 2 0 +138 f \N \N \N Tester 400 tester-400-ynzeo4 cus_TkZSfkqaA0ga1z 0 pending_payment bank_transfer 14 tester400pp@test.com US 2026-01-07 21:15:24.399423+00 2026-02-06 21:15:24.399423+00 2026-01-07 21:15:24.572218+00 2026-01-07 21:30:29.256809+00 1046 2026-01-07 21:30:29.26011+00 \N ~ \N \N 169 2 0 +138 f \N \N \N Tester 400 tester-400-ynzeo4 cus_TkZSfkqaA0ga1z 0 pending_payment bank_transfer 14 tester400pp@test.com US 2026-01-07 21:15:24.399423+00 2026-02-06 21:15:24.399423+00 2026-01-07 21:15:24.572218+00 2026-01-07 21:30:29.256809+00 1053 2026-01-07 22:34:02.066005+00 \N - \N 3 169 2 0 +136 f \N \N \N Tester 200 tester-200-fnvdza cus_TkYBQP1GWzDvgd 1000 active stripe 14 tester200@test.com PK 2026-01-07 20:11:53+00 2026-02-07 20:11:53+00 2026-01-07 20:11:27.727821+00 2026-01-07 20:24:48.155134+00 1039 2026-01-07 20:24:48.156556+00 \N ~ \N \N 167 2 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq cus_TkXZLOOuGtjXrJ 0 pending_payment bank_transfer 14 tester150@test.com PK \N \N 2026-01-07 19:34:04.726731+00 2026-01-07 19:34:11.703878+00 1027 2026-01-07 19:34:11.705801+00 \N ~ \N \N 164 4 0 +134 f \N \N \N Tester 150 tester-150-sgtzdq cus_TkXZLOOuGtjXrJ 2000 active stripe 14 tester150@test.com PK 2026-01-07 19:34:20+00 2026-02-07 19:34:20+00 2026-01-07 19:34:04.726731+00 2026-01-07 19:46:42.267485+00 1030 2026-01-07 19:46:42.269124+00 \N ~ \N \N 164 4 0 +139 f \N \N \N Tester 500 tester-500-lv8n3t \N 5000 pending_payment bank_transfer 14 tester500@test.com US \N \N 2026-01-07 21:41:07.958604+00 2026-01-07 21:41:07.958649+00 1048 2026-01-07 21:47:32.341113+00 \N ~ \N \N 170 5 0 +139 f \N \N \N Tester 500 tester-500-lv8n3t \N 5000 active paypal 14 tester500@test.com US \N \N 2026-01-07 21:41:07.958604+00 2026-01-07 21:47:32.345003+00 1049 2026-01-07 21:47:32.345914+00 \N ~ \N \N 170 5 0 +135 f \N \N \N Tester 160 tester-160-n2gx70 cus_TkXrynAI7a3Twm 0 pending_payment bank_transfer 14 tester160@test.com PK \N \N 2026-01-07 19:51:36.598818+00 2026-01-07 19:51:42.902397+00 1032 2026-01-07 19:51:42.904228+00 \N ~ \N \N 166 5 0 +135 t 2026-01-12 11:25:46.309251+00 2026-01-26 11:25:46.309251+00 \N Tester 160 tester-160-n2gx70 cus_TkXrynAI7a3Twm 5000 active stripe 14 tester160@test.com PK 2026-01-07 19:51:54+00 2026-02-07 19:51:54+00 2026-01-07 19:51:36.598818+00 2026-01-08 09:00:00.11771+00 1146 2026-01-12 11:25:46.310472+00 \N ~ \N 3 166 5 0 +133 f \N \N \N Tester 140 tester-140-twb705 cus_TkXIN2exB7SY4U 0 pending_payment bank_transfer 14 tester140@test.com PK \N \N 2026-01-07 19:17:05.219572+00 2026-01-07 19:17:11.357283+00 1022 2026-01-07 19:17:11.359576+00 \N ~ \N \N 163 2 0 +138 f \N \N \N Tester 400 tester-400-ynzeo4 cus_TkZSfkqaA0ga1z 0 pending_payment bank_transfer 14 tester400pp@test.com US 2026-01-07 21:15:24.399423+00 2026-02-06 21:15:24.399423+00 2026-01-07 21:15:24.572218+00 2026-01-07 21:30:29.256809+00 1054 2026-01-07 22:34:02.135573+00 \N - \N 3 169 2 0 +131 f \N \N \N tester 30 tester-30-8qhztb cus_TkWif9A5vyPuJl 0 pending_payment bank_transfer 14 tester30@test.com US \N \N 2026-01-07 18:39:24.028154+00 2026-01-07 18:40:32.084447+00 1056 2026-01-07 22:34:02.240234+00 \N - \N 3 161 2 0 +130 f \N \N \N tester 20 tester-20-x8lams \N 0 pending_payment bank_transfer 14 tester20@test.com PK \N \N 2026-01-07 18:37:42.498134+00 2026-01-07 18:37:42.49814+00 1057 2026-01-07 22:34:02.317998+00 \N - \N 3 160 2 0 +141 f \N \N \N Tester 1000 tester-1000-mjtugx \N 1000 pending_payment bank_transfer 14 tester1000@test.com CA \N \N 2026-01-07 23:32:07.351+00 2026-01-07 23:32:07.351009+00 1060 2026-01-07 23:33:04.893244+00 \N ~ \N \N 172 2 0 +141 f \N \N \N Tester 1000 tester-1000-mjtugx \N 1000 active paypal 14 tester1000@test.com CA \N \N 2026-01-07 23:32:07.351+00 2026-01-07 23:33:04.899342+00 1061 2026-01-07 23:33:04.90029+00 \N ~ \N \N 172 2 0 +141 t 2026-01-12 11:33:34.746175+00 2026-01-26 11:33:34.746175+00 \N Tester 1000 tester-1000-mjtugx \N 1000 active paypal 14 tester1000@test.com CA 2026-01-07 23:33:04.871028+00 2026-02-06 23:33:04.871028+00 2026-01-07 23:32:07.351+00 2026-01-08 09:00:00.274429+00 1147 2026-01-12 11:33:34.74728+00 \N ~ \N 3 172 2 0 +143 t 2026-01-12 11:37:54.598885+00 2026-01-26 11:37:54.598885+00 \N Salman Sadiq salman-sadiq-dkh6g3 cus_Tkey1Zm84HqAV2 1000 active stripe 14 bluesalman@hotmail.com PK 2026-01-08 03:13:17+00 2026-02-08 03:13:17+00 2026-01-08 02:42:38.501956+00 2026-01-08 09:00:00.311337+00 1149 2026-01-12 11:37:54.599971+00 \N ~ \N 3 174 2 0 +133 t 2026-01-12 11:37:54.644594+00 2026-01-26 11:37:54.644594+00 \N Tester 140 tester-140-twb705 cus_TkXIN2exB7SY4U 1000 active stripe 14 tester140@test.com PK 2026-01-07 19:17:19+00 2026-02-07 19:17:19+00 2026-01-07 19:17:05.219572+00 2026-01-08 09:00:00.243959+00 1150 2026-01-12 11:37:54.645584+00 \N ~ \N 3 163 2 0 +132 f \N \N \N tester 130 tester-130-lc5o3p cus_TkWrBA0ZGXW183 0 pending_payment bank_transfer 14 tester130@test.com PK \N \N 2026-01-07 18:49:25.293292+00 2026-01-07 18:49:30.936934+00 1018 2026-01-07 18:49:30.939159+00 \N ~ \N \N 162 2 0 +132 t 2026-01-12 11:37:54.693782+00 2026-01-26 11:37:54.693782+00 \N tester 130 tester-130-lc5o3p cus_TkWrBA0ZGXW183 1000 active bank_transfer 14 tester130@test.com PK 2026-01-07 19:14:51+00 2026-02-07 19:14:51+00 2026-01-07 18:49:25.293292+00 2026-01-08 09:00:00.33411+00 1151 2026-01-12 11:37:54.694811+00 \N ~ \N 3 162 2 0 +90 f \N \N \N Paid 2 paid-2 \N 1863 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1153 2026-01-12 11:48:20.263429+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1861 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1155 2026-01-12 11:48:45.966521+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1859 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1157 2026-01-12 11:49:14.441355+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1858 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1158 2026-01-12 11:49:27.762634+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1857 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1159 2026-01-12 11:49:41.896633+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1856 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1160 2026-01-12 11:49:56.780612+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1855 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1161 2026-01-12 11:50:12.755716+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1854 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1162 2026-01-12 11:50:27.230807+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1853 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1163 2026-01-12 11:50:42.1796+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1852 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1164 2026-01-12 11:50:55.348456+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1851 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1165 2026-01-12 11:51:07.22929+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1850 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1166 2026-01-12 11:51:20.91082+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1849 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1167 2026-01-12 11:51:36.234362+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1848 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1168 2026-01-12 11:51:50.190035+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1847 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1169 2026-01-12 11:52:03.006211+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1846 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1170 2026-01-12 11:52:18.581086+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1845 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1171 2026-01-12 11:52:32.652244+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1844 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1172 2026-01-12 23:41:13.496076+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1843 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1173 2026-01-12 23:41:28.642423+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1842 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1174 2026-01-12 23:41:43.866276+00 \N ~ \N \N 119 4 0 +90 f \N \N \N Paid 2 paid-2 \N 1841 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1175 2026-01-12 23:41:58.051987+00 \N ~ \N \N 119 4 0 +131 f \N \N \N tester 30 tester-30-8qhztb cus_TkWif9A5vyPuJl 0 pending_payment bank_transfer 14 tester30@test.com US \N \N 2026-01-07 18:39:24.028154+00 2026-01-07 18:40:32.084447+00 1055 2026-01-07 22:34:02.215738+00 \N - \N 3 161 2 0 +130 f \N \N \N tester 20 tester-20-x8lams \N 0 pending_payment bank_transfer 14 tester20@test.com PK \N \N 2026-01-07 18:37:42.498134+00 2026-01-07 18:37:42.49814+00 1058 2026-01-07 22:34:02.345905+00 \N - \N 3 160 2 0 +141 f \N \N \N Tester 1000 tester-1000-mjtugx \N 0 pending_payment bank_transfer 14 tester1000@test.com CA \N \N 2026-01-07 23:32:07.351+00 2026-01-07 23:32:07.351009+00 1059 2026-01-07 23:32:07.354709+00 \N + \N \N 172 2 0 +90 f \N \N \N Paid 2 paid-2 \N 1865 active bank_transfer 14 paid2@paid.com line 1 line 2 lahore punjab 12345 PK 2026-01-01 00:00:00+00 2026-01-31 23:59:59.05016+00 2025-12-09 02:03:03.119805+00 2026-01-08 00:30:00.050419+00 1062 2026-01-08 00:30:00.058092+00 \N ~ \N \N 119 4 0 +142 f \N \N \N Blue Salman blue-salman-1kp1gt \N 0 pending_payment bank_transfer 14 bluesalman@hotmail.com PK \N \N 2026-01-08 01:45:24.2024+00 2026-01-08 01:45:24.202409+00 1063 2026-01-08 01:45:24.206427+00 \N + \N \N 173 2 0 +142 f \N \N \N Blue Salman blue-salman-1kp1gt \N 0 pending_payment bank_transfer 14 bluesalman@hotmail.com PK \N \N 2026-01-08 01:45:24.2024+00 2026-01-08 01:45:24.202409+00 1064 2026-01-08 02:39:57.752108+00 \N ~ \N 3 \N 2 0 +142 f \N \N \N Blue Salman blue-salman-1kp1gt \N 0 pending_payment bank_transfer 14 bluesalman@hotmail.com PK \N \N 2026-01-08 01:45:24.2024+00 2026-01-08 01:45:24.202409+00 1065 2026-01-08 02:39:57.959583+00 \N - \N 3 \N 2 0 +143 f \N \N \N Salman Sadiq salman-sadiq-dkh6g3 \N 0 pending_payment bank_transfer 14 bluesalman@hotmail.com PK \N \N 2026-01-08 02:42:38.501956+00 2026-01-08 02:42:38.501968+00 1066 2026-01-08 02:42:38.5044+00 \N + \N \N 174 2 0 +143 f \N \N \N Salman Sadiq salman-sadiq-dkh6g3 cus_Tkey1Zm84HqAV2 1000 pending_payment bank_transfer 14 bluesalman@hotmail.com PK \N \N 2026-01-08 02:42:38.501956+00 2026-01-08 03:12:55.744752+00 1068 2026-01-08 03:13:21.116024+00 \N ~ \N \N 174 2 0 +143 f \N \N \N Salman Sadiq salman-sadiq-dkh6g3 cus_Tkey1Zm84HqAV2 1000 active stripe 14 bluesalman@hotmail.com PK \N \N 2026-01-08 02:42:38.501956+00 2026-01-08 03:13:21.120837+00 1069 2026-01-08 03:13:21.121486+00 \N ~ \N \N 174 2 0 +140 f \N \N \N Salman Sadiq salman-sadiq-mk1kdw \N 2000 active bank_transfer 14 salman@salman-sadiq.com PK \N \N 2026-01-07 21:54:17.430781+00 2026-01-07 22:16:42.341819+00 1070 2026-01-08 03:38:15.061032+00 \N ~ \N 3 \N 4 0 +140 f \N \N \N Salman Sadiq salman-sadiq-mk1kdw \N 2000 active bank_transfer 14 salman@salman-sadiq.com PK \N \N 2026-01-07 21:54:17.430781+00 2026-01-07 22:16:42.341819+00 1071 2026-01-08 03:38:15.199573+00 \N - \N 3 \N 4 0 +144 f \N \N \N Alorig alorig-qy5cvf \N 0 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2026-01-08 03:38:22.892096+00 2026-01-08 03:38:22.892102+00 1072 2026-01-08 03:38:22.894583+00 \N + \N \N 182 5 0 +143 f \N \N \N Salman Sadiq salman-sadiq-dkh6g3 cus_Tkey1Zm84HqAV2 0 pending_payment bank_transfer 14 bluesalman@hotmail.com PK \N \N 2026-01-08 02:42:38.501956+00 2026-01-08 03:12:55.744752+00 1067 2026-01-08 03:12:55.74656+00 \N ~ \N \N 174 2 0 +144 f \N \N \N Alorig alorig-qy5cvf cus_TkfQmjM3TBE47f 5000 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2026-01-08 03:38:22.892096+00 2026-01-08 03:41:11.330336+00 1074 2026-01-08 03:41:25.128491+00 \N ~ \N \N 182 5 0 +144 f \N \N \N Alorig alorig-qy5cvf cus_TkfQmjM3TBE47f 5000 active stripe 14 salman@alorig.com PK \N \N 2026-01-08 03:38:22.892096+00 2026-01-08 03:41:25.131331+00 1075 2026-01-08 03:41:25.131859+00 \N ~ \N \N 182 5 0 +145 f \N \N \N tacbit tacbit-5hnbn2 \N 0 pending_payment bank_transfer 14 tacbit.com@gmail.com PK \N \N 2026-01-08 04:07:29.766724+00 2026-01-08 04:07:29.76673+00 1076 2026-01-08 04:07:29.767412+00 \N + \N \N 183 4 0 +145 f \N \N \N tacbit tacbit-5hnbn2 \N 0 active bank_transfer 14 tacbit.com@gmail.com PK \N \N 2026-01-08 04:07:29.766724+00 2026-01-08 04:11:40.083852+00 1077 2026-01-08 04:11:40.084822+00 \N ~ \N 3 183 4 0 +145 f \N \N \N tacbit tacbit-5hnbn2 \N 2000 active bank_transfer 14 tacbit.com@gmail.com PK \N \N 2026-01-08 04:07:29.766724+00 2026-01-08 04:11:40.083852+00 1078 2026-01-08 04:11:40.088141+00 \N ~ \N 3 183 4 0 +135 f \N \N \N Tester 160 tester-160-n2gx70 cus_TkXrynAI7a3Twm 5000 active stripe 14 tester160@test.com PK 2026-01-07 19:51:54+00 2026-02-07 19:51:54+00 2026-01-07 19:51:36.598818+00 2026-01-08 09:00:00.11771+00 1079 2026-01-08 09:00:00.132847+00 \N ~ \N \N 166 5 0 +133 f \N \N \N Tester 140 tester-140-twb705 cus_TkXIN2exB7SY4U 1000 active stripe 14 tester140@test.com PK 2026-01-07 19:17:19+00 2026-02-07 19:17:19+00 2026-01-07 19:17:05.219572+00 2026-01-08 09:00:00.243959+00 1080 2026-01-08 09:00:00.247711+00 \N ~ \N \N 163 2 0 +144 f \N \N \N Alorig alorig-qy5cvf cus_TkfQmjM3TBE47f 5000 active stripe 14 salman@alorig.com PK 2026-01-08 03:41:20+00 2026-02-08 03:41:20+00 2026-01-08 03:38:22.892096+00 2026-01-08 09:00:00.262358+00 1081 2026-01-08 09:00:00.264656+00 \N ~ \N \N 182 5 0 +141 f \N \N \N Tester 1000 tester-1000-mjtugx \N 1000 active paypal 14 tester1000@test.com CA 2026-01-07 23:33:04.871028+00 2026-02-06 23:33:04.871028+00 2026-01-07 23:32:07.351+00 2026-01-08 09:00:00.274429+00 1082 2026-01-08 09:00:00.27633+00 \N ~ \N \N 172 2 0 +137 f \N \N \N Tester 300 tester-300-01hso1 cus_TkYvABxJE8dumo 1000 active stripe 14 tester300us@test.com US 2026-01-07 21:00:03+00 2026-02-07 21:00:03+00 2026-01-07 20:58:01.416106+00 2026-01-08 09:00:00.283763+00 1083 2026-01-08 09:00:00.285607+00 \N ~ \N \N 168 2 0 +139 f \N \N \N Tester 500 tester-500-lv8n3t \N 5000 active paypal 14 tester500@test.com US 2026-01-07 21:47:32.328924+00 2026-02-06 21:47:32.328924+00 2026-01-07 21:41:07.958604+00 2026-01-08 09:00:00.295194+00 1084 2026-01-08 09:00:00.2968+00 \N ~ \N \N 170 5 0 +143 f \N \N \N Salman Sadiq salman-sadiq-dkh6g3 cus_Tkey1Zm84HqAV2 1000 active stripe 14 bluesalman@hotmail.com PK 2026-01-08 03:13:17+00 2026-02-08 03:13:17+00 2026-01-08 02:42:38.501956+00 2026-01-08 09:00:00.311337+00 1085 2026-01-08 09:00:00.312654+00 \N ~ \N \N 174 2 0 +145 f \N \N \N tacbit tacbit-5hnbn2 \N 2000 active bank_transfer 14 tacbit.com@gmail.com PK 2026-01-08 04:07:29.59793+00 2026-02-07 04:07:29.59793+00 2026-01-08 04:07:29.766724+00 2026-01-08 09:00:00.319211+00 1086 2026-01-08 09:00:00.321198+00 \N ~ \N \N 183 4 0 +132 f \N \N \N tester 130 tester-130-lc5o3p cus_TkWrBA0ZGXW183 1000 active bank_transfer 14 tester130@test.com PK 2026-01-07 19:14:51+00 2026-02-07 19:14:51+00 2026-01-07 18:49:25.293292+00 2026-01-08 09:00:00.33411+00 1087 2026-01-08 09:00:00.336903+00 \N ~ \N \N 162 2 0 +146 f \N \N \N Alorig Systems alorig-systems-q2htbv \N 0 pending_payment bank_transfer 14 systems@alorig.com PK \N \N 2026-01-08 12:10:08.298636+00 2026-01-08 12:10:08.298649+00 1088 2026-01-08 12:10:08.308949+00 \N + \N \N 184 2 0 +144 f \N \N \N Alorig alorig-qy5cvf cus_TkfQmjM3TBE47f 5000 active stripe 14 salman@alorig.com PK 2026-01-08 03:41:20+00 2026-02-08 03:41:20+00 2026-01-08 03:38:22.892096+00 2026-01-08 09:00:00.262358+00 1089 2026-01-12 09:43:25.690407+00 \N ~ \N 3 \N 5 0 +144 f \N \N \N Alorig alorig-qy5cvf cus_TkfQmjM3TBE47f 0 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2026-01-08 03:38:22.892096+00 2026-01-08 03:41:11.330336+00 1073 2026-01-08 03:41:11.332226+00 \N ~ \N \N 182 5 0 +144 f \N \N \N Alorig alorig-qy5cvf cus_TkfQmjM3TBE47f 5000 active stripe 14 salman@alorig.com PK 2026-01-08 03:41:20+00 2026-02-08 03:41:20+00 2026-01-08 03:38:22.892096+00 2026-01-08 09:00:00.262358+00 1090 2026-01-12 09:43:25.91935+00 \N - \N 3 \N 5 0 +146 t 2026-01-12 09:48:00.784516+00 2026-01-26 09:48:00.784516+00 \N Alorig Systems alorig-systems-q2htbv \N 0 pending_payment bank_transfer 14 systems@alorig.com PK \N \N 2026-01-08 12:10:08.298636+00 2026-01-08 12:10:08.298649+00 1091 2026-01-12 09:48:00.787352+00 \N ~ \N 3 184 2 0 +103 t 2025-12-13 12:52:38.683092+00 2025-12-27 12:52:38.683092+00 \N Test Account - Limits \N 0 trial stripe 14 \N \N \N 2025-12-12 17:31:48.282845+00 2025-12-12 17:34:19.732204+00 1092 2026-01-12 10:06:29.843967+00 \N - \N \N \N 12 0 +100 t 2025-12-13 12:52:50.846757+00 2025-12-27 12:52:50.846757+00 \N sdadiaosdaodn sdadiaosdaodn \N 1000 active bank_transfer 14 axeeeeeee@cisidj.com PK \N \N 2025-12-09 06:00:45.905549+00 2025-12-09 06:13:29.351253+00 1093 2026-01-12 10:06:29.846196+00 \N - \N \N \N 2 0 +99 t 2025-12-09 06:12:07.782586+00 2025-12-23 06:12:07.782586+00 \N ascewwefwe ascewwefwe \N 0 pending_payment bank_transfer 14 sadwewdas@asdasdasd.com PK \N \N 2025-12-09 05:57:51.573029+00 2025-12-09 05:57:51.573037+00 1094 2026-01-12 10:06:29.847014+00 \N - \N \N \N 2 0 +98 t 2025-12-09 06:12:07.785812+00 2025-12-23 06:12:07.785812+00 \N asdasdasdafafjk asdasdasdafafjk \N 1000 trial bank_transfer 14 asdasdada@askdjkasjd.com \N \N 2025-12-09 05:56:32.674988+00 2025-12-09 05:56:32.675002+00 1095 2026-01-12 10:06:29.848386+00 \N - \N \N \N 1 0 +97 t 2025-12-09 06:12:07.787107+00 2025-12-23 06:12:07.787107+00 \N asdasdsad asdasdsad \N 0 pending_payment bank_transfer 14 asdsada@asdsad.com PK \N \N 2025-12-09 05:55:15.675964+00 2025-12-09 05:55:15.67597+00 1096 2026-01-12 10:06:29.849132+00 \N - \N \N \N 5 0 +96 t 2025-12-09 06:12:07.788721+00 2025-12-23 06:12:07.788721+00 \N asdasdasd asdasdasd \N 0 pending_payment bank_transfer 14 sadasdas@asdasda.com PK \N \N 2025-12-09 05:44:02.857643+00 2025-12-09 05:44:02.857659+00 1097 2026-01-12 10:06:29.849828+00 \N - \N \N \N 2 0 +95 t 2025-12-09 06:12:07.79003+00 2025-12-23 06:12:07.79003+00 \N sdsdds sdsdds \N 1000 trial bank_transfer 14 fdfsdf@dksdsds.com \N \N 2025-12-09 05:40:43.689251+00 2025-12-09 05:40:43.689265+00 1098 2026-01-12 10:06:29.850667+00 \N - \N \N \N 1 0 +94 t 2025-12-09 06:12:07.791002+00 2025-12-23 06:12:07.791002+00 \N asdsadasd asdsadasd \N 1000 trial bank_transfer 14 sdsdsd@asdsadas.com \N \N 2025-12-09 05:35:33.526448+00 2025-12-09 05:35:33.526457+00 1099 2026-01-12 10:06:29.851469+00 \N - \N \N \N 1 0 +93 t 2025-12-09 06:12:07.791963+00 2025-12-23 06:12:07.791963+00 \N sadsadasd sadsadasd \N 1000 trial bank_transfer 14 asdasda@adsad.com \N \N 2025-12-09 05:31:13.840279+00 2025-12-09 05:31:13.840293+00 1100 2026-01-12 10:06:29.852222+00 \N - \N \N \N 1 0 +92 t 2025-12-09 06:12:07.793206+00 2025-12-23 06:12:07.793206+00 \N Free Salman free-salman \N 1000 trial bank_transfer 14 free@salman.com \N \N 2025-12-09 05:21:40.07927+00 2025-12-09 05:21:40.079286+00 1101 2026-01-12 10:06:29.852881+00 \N - \N \N \N 1 0 +91 t 2025-12-09 06:12:07.795038+00 2025-12-23 06:12:07.795038+00 \N Salman Alorig salman-alorig \N 0 pending_payment bank_transfer 14 salman@alorig.com PK \N \N 2025-12-09 05:07:26.824419+00 2025-12-09 05:07:26.824432+00 1102 2026-01-12 10:06:29.853526+00 \N - \N \N \N 2 0 +89 t 2025-12-28 23:30:50.570352+00 2026-01-11 23:30:50.570352+00 \N Paid 1 paid-1 \N 0 active bank_transfer 14 paid1@paid.com \N \N 2025-12-09 01:23:21.533612+00 2025-12-09 01:52:55.679624+00 1103 2026-01-12 10:06:29.854327+00 \N - \N \N \N 4 0 +88 t 2025-12-09 00:39:29.412583+00 2025-12-23 00:39:29.412583+00 \N asdasdsa asdasdsa \N 1000 trial bank_transfer 14 asdas@sdsad.com \N \N 2025-12-09 00:03:24.295864+00 2025-12-09 00:03:24.295875+00 1104 2026-01-12 10:06:29.855106+00 \N - \N \N \N 1 0 +87 t 2025-12-09 00:39:29.41681+00 2025-12-23 00:39:29.41681+00 \N Paid's Account paid-user-1765236936.066345 \N 1000 active stripe 14 billing@example.com 123 Main Street Karachi PK \N \N 2025-12-08 23:35:36.066838+00 2025-12-08 23:35:36.085204+00 1105 2026-01-12 10:06:29.855828+00 \N - \N \N \N 2 0 +86 t 2025-12-09 00:39:29.420024+00 2025-12-23 00:39:29.420024+00 \N Free's Account free-trial-1765236935.883199 \N 1000 trial stripe 14 \N \N \N 2025-12-08 23:35:35.88385+00 2025-12-08 23:35:35.883857+00 1106 2026-01-12 10:06:29.856574+00 \N - \N \N \N 1 0 +85 t 2025-12-08 23:35:35.652401+00 2025-12-22 23:35:35.652401+00 \N Paid's Account paid-user-1765236922.413418 \N 0 pending_payment stripe 14 billing@example.com 123 Main Street Karachi PK \N \N 2025-12-08 23:35:22.413812+00 2025-12-08 23:35:22.413817+00 1107 2026-01-12 10:06:29.857319+00 \N - \N \N \N 2 0 +84 t 2025-12-08 23:35:35.624964+00 2025-12-22 23:35:35.624964+00 \N Free's Account free-trial-1765236922.213135 \N 1000 trial stripe 14 \N \N \N 2025-12-08 23:35:22.213731+00 2025-12-08 23:35:22.213736+00 1108 2026-01-12 10:06:29.858034+00 \N - \N \N \N 1 0 +83 t 2025-12-08 23:35:22.017086+00 2025-12-22 23:35:22.017086+00 \N Paid's Account paid-user-1765236886.790393 \N 0 pending_payment stripe 14 billing@example.com 123 Main Street Karachi PK \N \N 2025-12-08 23:34:46.790848+00 2025-12-08 23:34:46.790853+00 1109 2026-01-12 10:06:29.858934+00 \N - \N \N \N 2 0 +82 t 2025-12-08 23:35:21.986575+00 2025-12-22 23:35:21.986575+00 \N Free's Account free-trial-1765236886.557967 \N 1000 trial stripe 14 \N \N \N 2025-12-08 23:34:46.558658+00 2025-12-08 23:34:46.558669+00 1110 2026-01-12 10:06:29.859692+00 \N - \N \N \N 1 0 +80 t 2025-12-08 23:34:46.354291+00 2025-12-22 23:34:46.354291+00 \N Free's Account free-trial-1765236768.239178 \N 1000 trial stripe 14 \N \N \N 2025-12-08 23:32:48.239665+00 2025-12-08 23:32:48.23967+00 1111 2026-01-12 10:06:29.860572+00 \N - \N \N \N 1 0 +78 t 2025-12-08 23:32:48.023042+00 2025-12-22 23:32:48.023042+00 \N Free's Account free-trial-1765236734.737537 \N 1000 trial stripe 14 \N \N \N 2025-12-08 23:32:14.738109+00 2025-12-08 23:32:14.738115+00 1112 2026-01-12 10:06:29.861567+00 \N - \N \N \N 1 0 +77 t 2025-12-08 23:32:14.518655+00 2025-12-22 23:32:14.518655+00 \N Free's Account free-trial-1765236716.736076 \N 1000 trial stripe 14 \N \N \N 2025-12-08 23:31:56.738159+00 2025-12-08 23:31:56.738169+00 1113 2026-01-12 10:06:29.862317+00 \N - \N \N \N 1 0 +74 t 2025-12-09 00:39:29.421968+00 2025-12-23 00:39:29.421968+00 \N Test Account test-account \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:49:45.814379+00 2025-12-08 18:49:45.814392+00 1114 2026-01-12 10:06:29.863111+00 \N - \N \N \N 1 0 +73 t 2025-12-09 00:39:29.422994+00 2025-12-23 00:39:29.422994+00 \N asdasdadaadsasd asdasdadaadsasd \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:28:52.692428+00 2025-12-08 18:28:52.69244+00 1115 2026-01-12 10:06:29.86372+00 \N - \N \N \N 1 0 +72 t 2025-12-09 00:39:29.424025+00 2025-12-23 00:39:29.424025+00 \N adsasdsa adsasdsa \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:24:10.678199+00 2025-12-08 18:24:10.678217+00 1116 2026-01-12 10:06:29.864395+00 \N - \N \N \N 1 0 +39 t 2025-12-09 00:39:29.424928+00 2025-12-23 00:39:29.424928+00 \N asdklasjdk asdklasjdk \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:21:47.623842+00 2025-12-08 18:21:47.623852+00 1117 2026-01-12 10:06:29.865247+00 \N - \N \N \N 1 0 +38 t 2025-12-09 00:39:29.425965+00 2025-12-23 00:39:29.425965+00 \N abc abc \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:16:11.894674+00 2025-12-08 18:16:11.894688+00 1118 2026-01-12 10:06:29.866012+00 \N - \N \N \N 1 0 +37 t 2025-12-09 00:39:29.426862+00 2025-12-23 00:39:29.426862+00 \N Debug Test debug-test \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:14:28.655447+00 2025-12-08 18:14:28.655458+00 1119 2026-01-12 10:06:29.866756+00 \N - \N \N \N 1 0 +36 t 2025-12-09 00:39:29.427876+00 2025-12-23 00:39:29.427876+00 \N Alorig 6 alorig-6 \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:08:21.885756+00 2025-12-08 18:08:21.885766+00 1120 2026-01-12 10:06:29.867491+00 \N - \N \N \N 1 0 +35 t 2025-12-09 00:39:29.429062+00 2025-12-23 00:39:29.429062+00 \N Alorig 5 alorig-5 \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 18:04:12.480318+00 2025-12-08 18:04:12.480327+00 1121 2026-01-12 10:06:29.868251+00 \N - \N \N \N 1 0 +34 t 2025-12-09 00:39:29.430192+00 2025-12-23 00:39:29.430192+00 \N New User new-user \N 1000 trial bank_transfer 14 \N \N \N 2025-12-08 17:44:18.620096+00 2025-12-08 17:44:18.620103+00 1122 2026-01-12 10:06:29.868982+00 \N - \N \N \N 1 0 +33 t 2025-12-09 00:39:29.431533+00 2025-12-23 00:39:29.431533+00 \N Test User456 test-user456 \N 100 trial bank_transfer 14 \N \N \N 2025-12-08 17:43:33.282346+00 2025-12-08 17:43:33.282361+00 1123 2026-01-12 10:06:29.869639+00 \N - \N \N \N 1 0 +32 t 2025-12-08 08:56:13.981751+00 2025-12-22 08:56:13.981751+00 \N Alorig 2 alorig-2 \N 100 trial bank_transfer 14 \N \N \N 2025-12-08 08:52:13.331065+00 2025-12-08 08:52:13.331076+00 1124 2026-01-12 10:06:29.8703+00 \N - \N \N \N 1 0 +31 t 2025-12-08 08:56:13.985052+00 2025-12-22 08:56:13.985052+00 \N Alorig1 alorig1 \N 0 pending_payment bank_transfer 14 \N \N \N 2025-12-08 08:37:17.702577+00 2025-12-08 08:37:17.702588+00 1125 2026-01-12 10:06:29.871017+00 \N - \N \N \N 4 0 +30 t 2025-12-08 08:56:13.986679+00 2025-12-22 08:56:13.986679+00 \N TacBit tacbit \N 100 trial stripe 14 \N \N \N 2025-12-08 06:13:38.583555+00 2025-12-08 06:22:32.243818+00 1126 2026-01-12 10:06:29.871631+00 \N - \N \N \N 1 0 +29 t 2025-12-28 23:30:50.581737+00 2026-01-11 23:30:50.581737+00 \N Home G8 home-g8 \N 8000 active stripe 14 \N \N \N 2025-12-07 08:57:12.044018+00 2025-12-07 14:11:16.788723+00 1127 2026-01-12 10:06:29.872289+00 \N - \N \N \N 2 0 +28 t 2025-12-08 08:56:13.988502+00 2025-12-22 08:56:13.988502+00 \N Auto Remote auto-remote \N 0 trial stripe 14 \N \N \N 2025-12-07 07:49:39.402328+00 2025-12-07 07:49:39.402335+00 1128 2026-01-12 10:06:29.872943+00 \N - \N \N \N 6 0 +27 t 2025-12-08 08:56:13.990073+00 2025-12-22 08:56:13.990073+00 \N testing ssytem testing-ssytem \N 0 trial stripe 14 \N \N \N 2025-12-07 07:45:23.70806+00 2025-12-07 07:45:23.708069+00 1129 2026-01-12 10:06:29.873516+00 \N - \N \N \N 6 0 +26 t 2025-12-08 08:56:13.99266+00 2025-12-22 08:56:13.99266+00 \N Slug Fix slug-fix \N 0 trial stripe 14 \N \N \N 2025-12-07 07:44:18.847776+00 2025-12-07 07:44:18.847789+00 1130 2026-01-12 10:06:29.874214+00 \N - \N \N \N 6 0 +25 t 2025-12-08 08:56:13.993772+00 2025-12-22 08:56:13.993772+00 \N Salman Raza Sadiq salman-raza-sadiq \N 0 trial stripe 14 \N \N \N 2025-12-07 07:38:47.707388+00 2025-12-07 07:38:47.707398+00 1131 2026-01-12 10:06:29.874881+00 \N - \N \N \N 6 0 +19 t 2025-12-07 07:33:43.511567+00 2025-12-21 07:33:43.511567+00 \N Salman Sadiq salman-sadiq \N 4500 active stripe 14 \N \N \N 2025-11-20 01:34:16.481172+00 2025-11-20 01:36:16.781712+00 1132 2026-01-12 10:06:29.875494+00 \N - \N \N \N 5 0 +18 t 2025-12-07 02:21:30.884172+00 2025-12-21 02:21:30.884172+00 \N test3 test3 \N 0 trial stripe 14 \N \N \N 2025-11-14 16:08:18.880277+00 2025-11-14 16:08:18.880285+00 1133 2026-01-12 10:06:29.87613+00 \N - \N \N \N 1 0 +17 t 2025-12-07 02:21:30.886458+00 2025-12-21 02:21:30.886458+00 \N test2 test2 \N 0 trial stripe 14 \N \N \N 2025-11-14 16:03:17.312034+00 2025-11-14 16:03:17.312057+00 1134 2026-01-12 10:06:29.876789+00 \N - \N \N \N 1 0 +\. + + +-- +-- Data for Name: igny8_credit_cost_config; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_credit_cost_config (operation_type, display_name, description, is_active, calculation_mode, flat_rate_credits, per_item_credits, per_item_unit, base_credits) FROM stdin; +clustering Keyword Clustering AI-powered keyword clustering into semantic groups t flat_rate 10 \N 1 1 +content_generation Content Generation AI-powered article content generation t per_item \N 0.01 100 1 +idea_generation Content Ideas Generation Generate content ideas from keyword clusters t per_item \N 2.00 1 1 +image_generation Image Generation AI-powered image generation t per_image 5 \N 1 1 +image_prompt_extraction Image Prompt Extraction Extract image prompts from content t per_tokens \N \N 1 1 +site_structure_generation Site Structure Generation Generate complete site blueprint f per_tokens \N \N 1 1 +site_page_generation Site Page Generation Generate site pages from blueprint f per_tokens \N \N 1 1 +linking Internal Linking AI-powered internal link suggestions f flat_rate 8 \N 1 1 +optimization Content Optimization AI-powered content optimization f per_item \N 0.01 100 1 +reparse Content Reparse Reparse existing content f flat_rate 1 \N 1 1 +\. + + +-- +-- Data for Name: igny8_credit_packages; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_credit_packages (id, name, slug, credits, price, discount_percentage, stripe_product_id, stripe_price_id, paypal_plan_id, is_active, is_featured, description, features, sort_order, created_at, updated_at) FROM stdin; +1 Starter Pack starter-pack 1000 9.99 0 \N \N \N t f Perfect for trying out the platform [] 1 2025-12-04 23:47:42.610533+00 2025-12-04 23:47:42.610554+00 +3 Business Pack business-pack 15000 99.99 30 \N \N \N t f Ideal for established businesses [] 3 2025-12-04 23:47:42.617445+00 2025-12-04 23:47:42.617453+00 +4 Enterprise Pack enterprise-pack 50000 299.99 40 \N \N \N t t Maximum value for high-volume users [] 4 2025-12-04 23:47:42.619287+00 2025-12-04 23:47:42.619296+00 +\. + + +-- +-- Data for Name: igny8_credit_transactions; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_credit_transactions (id, updated_at, transaction_type, amount, balance_after, description, metadata, created_at, tenant_id, reference_id, payment_id) FROM stdin; +527 2025-12-29 02:39:14.141213+00 deduction -3 651 clustering: 2715 tokens (2537 in, 178 out) = 3 credits {"count": 1, "function_name": "auto_cluster", "clusters_created": 1, "keywords_updated": 6} 2025-12-29 02:39:14.1413+00 90 \N +528 2025-12-29 02:39:34.9522+00 deduction -5 646 idea_generation: 4197 tokens (1753 in, 2444 out) = 5 credits {"count": 4, "function_name": "generate_ideas", "ideas_created": 4, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:39:34.952227+00 90 \N +529 2025-12-29 02:40:10.312593+00 deduction -6 640 content_generation: 5558 tokens (2200 in, 3358 out) = 6 credits {"count": 1, "task_id": 288, "content_id": 200, "word_count": 1772, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:40:10.312643+00 90 \N +530 2025-12-29 02:40:39.140263+00 deduction -6 634 content_generation: 5659 tokens (2041 in, 3618 out) = 6 credits {"count": 1, "task_id": 287, "content_id": 201, "word_count": 1910, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:40:39.140296+00 90 \N +531 2025-12-29 02:41:08.576495+00 deduction -6 628 content_generation: 5198 tokens (2101 in, 3097 out) = 6 credits {"count": 1, "task_id": 286, "content_id": 202, "word_count": 1778, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:41:08.576517+00 90 \N +532 2025-12-29 02:41:44.664444+00 deduction -6 622 content_generation: 5364 tokens (2093 in, 3271 out) = 6 credits {"count": 1, "task_id": 285, "content_id": 203, "word_count": 1732, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:41:44.664463+00 90 \N +533 2025-12-29 02:41:57.554114+00 deduction -2 620 image_prompt_extraction: 1407 tokens (607 in, 800 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:41:57.554137+00 90 \N +534 2025-12-29 02:42:06.162079+00 deduction -2 618 image_prompt_extraction: 1295 tokens (613 in, 682 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:42:06.162101+00 90 \N +535 2025-12-29 02:42:14.314136+00 deduction -2 616 image_prompt_extraction: 1305 tokens (609 in, 696 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:42:14.314157+00 90 \N +536 2025-12-29 02:42:22.303018+00 deduction -2 614 image_prompt_extraction: 1301 tokens (599 in, 702 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:42:22.303038+00 90 \N +537 2026-01-01 00:00:00.41308+00 subscription 10000 10323 Monthly credit replenishment - Internal (System/Superuser) plan {"plan_id": 6, "plan_name": "Internal (System/Superuser)", "monthly_credits": 10000, "replenishment_date": "2026-01-01T00:00:00.394050+00:00"} 2026-01-01 00:00:00.413126+00 5 \N +538 2026-01-01 00:00:00.462641+00 subscription 2000 2614 Monthly credit replenishment - Growth plan {"plan_id": 4, "plan_name": "Growth", "monthly_credits": 2000, "replenishment_date": "2026-01-01T00:00:00.458237+00:00"} 2026-01-01 00:00:00.462676+00 90 \N +539 2026-01-01 00:04:58.55052+00 deduction -3 2611 clustering: 2782 tokens (2535 in, 247 out) = 3 credits {"count": 2, "function_name": "auto_cluster", "clusters_created": 2, "keywords_updated": 6} 2026-01-01 00:04:58.550551+00 90 \N +540 2026-01-01 00:05:24.293111+00 deduction -5 2606 idea_generation: 4078 tokens (1715 in, 2363 out) = 5 credits {"count": 4, "function_name": "generate_ideas", "ideas_created": 4, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:05:24.293132+00 90 \N +541 2026-01-01 00:05:46.72621+00 deduction -5 2601 idea_generation: 4154 tokens (1700 in, 2454 out) = 5 credits {"count": 4, "function_name": "generate_ideas", "ideas_created": 4, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:05:46.726229+00 90 \N +542 2026-01-01 00:06:25.709145+00 deduction -6 2595 content_generation: 5159 tokens (2167 in, 2992 out) = 6 credits {"count": 1, "task_id": 296, "content_id": 204, "word_count": 1683, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:06:25.70918+00 90 \N +543 2026-01-01 00:06:52.453844+00 deduction -6 2589 content_generation: 5115 tokens (2023 in, 3092 out) = 6 credits {"count": 1, "task_id": 295, "content_id": 205, "word_count": 1728, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:06:52.453864+00 90 \N +544 2026-01-01 00:07:24.669555+00 deduction -6 2583 content_generation: 5850 tokens (2054 in, 3796 out) = 6 credits {"count": 1, "task_id": 294, "content_id": 206, "word_count": 2114, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:07:24.669583+00 90 \N +545 2026-01-01 00:07:59.699928+00 deduction -7 2576 content_generation: 6056 tokens (2028 in, 4028 out) = 7 credits {"count": 1, "task_id": 293, "content_id": 207, "word_count": 2222, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:07:59.699977+00 90 \N +546 2026-01-01 00:08:32.549312+00 deduction -6 2570 content_generation: 5228 tokens (2189 in, 3039 out) = 6 credits {"count": 1, "task_id": 292, "content_id": 208, "word_count": 1643, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:08:32.549337+00 90 \N +547 2026-01-01 00:09:15.286884+00 deduction -6 2564 content_generation: 5916 tokens (2019 in, 3897 out) = 6 credits {"count": 1, "task_id": 291, "content_id": 209, "word_count": 2119, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:09:15.286911+00 90 \N +548 2026-01-01 00:09:48.76696+00 deduction -6 2558 content_generation: 5564 tokens (2074 in, 3490 out) = 6 credits {"count": 1, "task_id": 290, "content_id": 210, "word_count": 1878, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:09:48.766981+00 90 \N +549 2026-01-01 00:10:19.192952+00 deduction -6 2552 content_generation: 5256 tokens (2068 in, 3188 out) = 6 credits {"count": 1, "task_id": 289, "content_id": 211, "word_count": 1670, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:19.19298+00 90 \N +550 2026-01-01 00:10:32.326644+00 deduction -2 2550 image_prompt_extraction: 1316 tokens (608 in, 708 out) = 2 credits {"count": 3, "function_name": "generate_image_prompts", "prompts_created": 3, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:32.326667+00 90 \N +551 2026-01-01 00:10:39.733542+00 deduction -2 2548 image_prompt_extraction: 1307 tokens (596 in, 711 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:39.733573+00 90 \N +552 2026-01-01 00:10:47.058805+00 deduction -2 2546 image_prompt_extraction: 1296 tokens (590 in, 706 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:47.058834+00 90 \N +553 2026-01-01 00:10:56.613206+00 deduction -2 2544 image_prompt_extraction: 1362 tokens (606 in, 756 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:56.613226+00 90 \N +554 2026-01-01 00:11:05.938944+00 deduction -2 2542 image_prompt_extraction: 1362 tokens (605 in, 757 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:05.938981+00 90 \N +555 2026-01-01 00:11:13.468177+00 deduction -2 2540 image_prompt_extraction: 1158 tokens (450 in, 708 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:13.468207+00 90 \N +556 2026-01-01 00:11:21.34208+00 deduction -2 2538 image_prompt_extraction: 1329 tokens (609 in, 720 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:21.342105+00 90 \N +557 2026-01-01 00:11:29.146435+00 deduction -2 2536 image_prompt_extraction: 1335 tokens (610 in, 725 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:29.146519+00 90 \N +558 2026-01-03 17:52:05.373192+00 deduction -6 1876 content_generation: 5487 tokens (2167 in, 3320 out) = 6 credits {"count": 1, "task_id": 296, "content_id": 212, "word_count": 1837, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-03 17:52:05.373223+00 90 \N +559 2026-01-03 17:52:34.680438+00 deduction -2 1874 image_prompt_extraction: 1315 tokens (595 in, 720 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-03 17:52:34.680459+00 90 \N +560 2026-01-03 18:10:26.109281+00 deduction -2 1872 image_prompt_extraction: 1254 tokens (590 in, 664 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-03 18:10:26.109301+00 90 \N +561 2026-01-03 18:14:28.85609+00 deduction -2 1870 image_prompt_extraction: 1318 tokens (609 in, 709 out) = 2 credits {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-03 18:14:28.85612+00 90 \N +562 2026-01-06 00:11:36.550709+00 deduction -5 1865 Test image generation {} 2026-01-06 00:11:36.550753+00 90 \N +586 2026-01-12 11:48:06.362921+00 deduction -1 1864 Image generation: 10 Best Back Massagers for Pain Relief in 2023 {"size": "1024x1024", "model": "runware:97@1", "image_id": 545, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:06.362971+00 90 \N +587 2026-01-12 11:48:20.265273+00 deduction -1 1863 Image generation: 10 Best Back Massagers for Pain Relief in 2023 {"size": "1792x1024", "model": "runware:97@1", "image_id": 544, "provider": "runware", "content_id": 216, "image_type": "featured"} 2026-01-12 11:48:20.265306+00 90 \N +588 2026-01-12 11:48:32.928017+00 deduction -1 1862 Image generation: 10 Best Back Massagers for Pain Relief in 2023 {"size": "1792x1024", "model": "runware:97@1", "image_id": 546, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:32.928036+00 90 \N +589 2026-01-12 11:48:45.968113+00 deduction -1 1861 Image generation: 10 Best Back Massagers for Pain Relief in 2023 {"size": "1024x1024", "model": "runware:97@1", "image_id": 547, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:45.968136+00 90 \N +590 2026-01-12 11:48:58.614413+00 deduction -1 1860 Image generation: 10 Best Back Massagers for Pain Relief in 2023 {"size": "1792x1024", "model": "runware:97@1", "image_id": 548, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:58.614458+00 90 \N +591 2026-01-12 11:49:14.443325+00 deduction -1 1859 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1024x1024", "model": "runware:97@1", "image_id": 540, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:49:14.44335+00 90 \N +592 2026-01-12 11:49:27.766234+00 deduction -1 1858 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1792x1024", "model": "runware:97@1", "image_id": 539, "provider": "runware", "content_id": 215, "image_type": "featured"} 2026-01-12 11:49:27.766262+00 90 \N +593 2026-01-12 11:49:41.898911+00 deduction -1 1857 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1792x1024", "model": "runware:97@1", "image_id": 541, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:49:41.898953+00 90 \N +594 2026-01-12 11:49:56.782125+00 deduction -1 1856 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1024x1024", "model": "runware:97@1", "image_id": 542, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:49:56.782144+00 90 \N +595 2026-01-12 11:50:12.757407+00 deduction -1 1855 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1792x1024", "model": "runware:97@1", "image_id": 543, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:50:12.757432+00 90 \N +596 2026-01-12 11:50:27.232592+00 deduction -1 1854 Image generation: How to Choose the Best Back Massager for Your Need {"size": "1024x1024", "model": "runware:97@1", "image_id": 535, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:50:27.232617+00 90 \N +597 2026-01-12 11:50:42.181392+00 deduction -1 1853 Image generation: How to Choose the Best Back Massager for Your Need {"size": "1792x1024", "model": "runware:97@1", "image_id": 534, "provider": "runware", "content_id": 214, "image_type": "featured"} 2026-01-12 11:50:42.18142+00 90 \N +598 2026-01-12 11:50:55.350627+00 deduction -1 1852 Image generation: How to Choose the Best Back Massager for Your Need {"size": "1792x1024", "model": "runware:97@1", "image_id": 536, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:50:55.350654+00 90 \N +599 2026-01-12 11:51:07.230761+00 deduction -1 1851 Image generation: How to Choose the Best Back Massager for Your Need {"size": "1024x1024", "model": "runware:97@1", "image_id": 537, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:51:07.230796+00 90 \N +600 2026-01-12 11:51:20.912464+00 deduction -1 1850 Image generation: How to Choose the Best Back Massager for Your Need {"size": "1792x1024", "model": "runware:97@1", "image_id": 538, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:51:20.91249+00 90 \N +601 2026-01-12 11:51:36.235891+00 deduction -1 1849 Image generation: Complete Guide to the Best Back Massagers for Pain {"size": "1024x1024", "model": "runware:97@1", "image_id": 530, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:51:36.23591+00 90 \N +602 2026-01-12 11:51:50.191826+00 deduction -1 1848 Image generation: Complete Guide to the Best Back Massagers for Pain {"size": "1792x1024", "model": "runware:97@1", "image_id": 529, "provider": "runware", "content_id": 213, "image_type": "featured"} 2026-01-12 11:51:50.191855+00 90 \N +603 2026-01-12 11:52:03.009581+00 deduction -1 1847 Image generation: Complete Guide to the Best Back Massagers for Pain {"size": "1792x1024", "model": "runware:97@1", "image_id": 531, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:52:03.009611+00 90 \N +604 2026-01-12 11:52:18.582391+00 deduction -1 1846 Image generation: Complete Guide to the Best Back Massagers for Pain {"size": "1024x1024", "model": "runware:97@1", "image_id": 532, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:52:18.58241+00 90 \N +605 2026-01-12 11:52:32.654046+00 deduction -1 1845 Image generation: Complete Guide to the Best Back Massagers for Pain {"size": "1792x1024", "model": "runware:97@1", "image_id": 533, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:52:32.654072+00 90 \N +606 2026-01-12 23:41:13.51092+00 deduction -1 1844 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1024x1024", "model": "runware:97@1", "image_id": 550, "provider": "runware", "content_id": 217, "image_type": "in_article"} 2026-01-12 23:41:13.510998+00 90 \N +607 2026-01-12 23:41:28.645179+00 deduction -1 1843 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1792x1024", "model": "runware:97@1", "image_id": 549, "provider": "runware", "content_id": 217, "image_type": "featured"} 2026-01-12 23:41:28.645225+00 90 \N +608 2026-01-12 23:41:43.867726+00 deduction -1 1842 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1792x1024", "model": "runware:97@1", "image_id": 551, "provider": "runware", "content_id": 217, "image_type": "in_article"} 2026-01-12 23:41:43.867746+00 90 \N +609 2026-01-12 23:41:58.053531+00 deduction -1 1841 Image generation: Back Massagers vs. Traditional Massage: Which Is B {"size": "1024x1024", "model": "runware:97@1", "image_id": 552, "provider": "runware", "content_id": 217, "image_type": "in_article"} 2026-01-12 23:41:58.053555+00 90 \N +\. + + +-- +-- Data for Name: igny8_credit_usage_logs; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_credit_usage_logs (id, updated_at, operation_type, credits_used, cost_usd, model_used, tokens_input, tokens_output, related_object_type, related_object_id, metadata, created_at, tenant_id, site_id) FROM stdin; +705 2026-01-12 23:41:58.054224+00 image_generation 1 0.0090 runware:97@1 \N \N image 552 {"size": "1024x1024", "model": "runware:97@1", "image_id": 552, "provider": "runware", "content_id": 217, "image_type": "in_article"} 2026-01-12 23:41:58.05428+00 90 21 +702 2026-01-12 23:41:13.516366+00 image_generation 1 0.0090 runware:97@1 \N \N image 550 {"size": "1024x1024", "model": "runware:97@1", "image_id": 550, "provider": "runware", "content_id": 217, "image_type": "in_article"} 2026-01-12 23:41:13.516635+00 90 21 +700 2026-01-12 11:52:18.583129+00 image_generation 1 0.0090 runware:97@1 \N \N image 532 {"size": "1024x1024", "model": "runware:97@1", "image_id": 532, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:52:18.583165+00 90 21 +697 2026-01-12 11:51:36.236721+00 image_generation 1 0.0090 runware:97@1 \N \N image 530 {"size": "1024x1024", "model": "runware:97@1", "image_id": 530, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:51:36.236798+00 90 21 +694 2026-01-12 11:50:55.351622+00 image_generation 1 0.0090 runware:97@1 \N \N image 536 {"size": "1792x1024", "model": "runware:97@1", "image_id": 536, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:50:55.351668+00 90 21 +685 2026-01-12 11:48:45.968943+00 image_generation 1 0.0090 runware:97@1 \N \N image 547 {"size": "1024x1024", "model": "runware:97@1", "image_id": 547, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:45.968995+00 90 21 +682 2026-01-12 11:48:06.365168+00 image_generation 1 0.0090 runware:97@1 \N \N image 545 {"size": "1024x1024", "model": "runware:97@1", "image_id": 545, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:06.365274+00 90 21 +688 2026-01-12 11:49:27.767009+00 image_generation 1 0.0090 runware:97@1 \N \N image 539 {"size": "1792x1024", "model": "runware:97@1", "image_id": 539, "provider": "runware", "content_id": 215, "image_type": "featured"} 2026-01-12 11:49:27.767057+00 90 21 +691 2026-01-12 11:50:12.758539+00 image_generation 1 0.0090 runware:97@1 \N \N image 543 {"size": "1792x1024", "model": "runware:97@1", "image_id": 543, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:50:12.758585+00 90 21 +648 2025-12-29 02:39:14.144952+00 clustering 3 0.0050 gpt-5.1 2537 178 cluster \N {"count": 1, "function_name": "auto_cluster", "clusters_created": 1, "keywords_updated": 6} 2025-12-29 02:39:14.14534+00 90 21 +649 2025-12-29 02:39:34.952799+00 idea_generation 5 0.0266 gpt-5.1 1753 2444 content_idea \N {"count": 4, "function_name": "generate_ideas", "ideas_created": 4, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:39:34.952842+00 90 21 +650 2025-12-29 02:40:10.313444+00 content_generation 6 0.0363 gpt-5.1 2200 3358 content 288 {"count": 1, "task_id": 288, "content_id": 200, "word_count": 1772, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:40:10.313512+00 90 21 +651 2025-12-29 02:40:39.140755+00 content_generation 6 0.0387 gpt-5.1 2041 3618 content 287 {"count": 1, "task_id": 287, "content_id": 201, "word_count": 1910, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:40:39.140788+00 90 21 +652 2025-12-29 02:41:08.576985+00 content_generation 6 0.0336 gpt-5.1 2101 3097 content 286 {"count": 1, "task_id": 286, "content_id": 202, "word_count": 1778, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:41:08.577027+00 90 21 +653 2025-12-29 02:41:44.664857+00 content_generation 6 0.0353 gpt-5.1 2093 3271 content 285 {"count": 1, "task_id": 285, "content_id": 203, "word_count": 1732, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:41:44.664887+00 90 21 +654 2025-12-29 02:41:57.554567+00 image_prompt_extraction 2 0.0088 gpt-5.1 607 800 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:41:57.554604+00 90 21 +655 2025-12-29 02:42:06.162608+00 image_prompt_extraction 2 0.0076 gpt-5.1 613 682 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:42:06.162641+00 90 21 +656 2025-12-29 02:42:14.314654+00 image_prompt_extraction 2 0.0077 gpt-5.1 609 696 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:42:14.314688+00 90 21 +657 2025-12-29 02:42:22.303472+00 image_prompt_extraction 2 0.0078 gpt-5.1 599 702 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2025-12-29 02:42:22.303505+00 90 21 +658 2026-01-01 00:04:58.552006+00 clustering 3 0.0056 gpt-5.1 2535 247 cluster \N {"count": 2, "function_name": "auto_cluster", "clusters_created": 2, "keywords_updated": 6} 2026-01-01 00:04:58.552205+00 90 21 +659 2026-01-01 00:05:24.293626+00 idea_generation 5 0.0258 gpt-5.1 1715 2363 content_idea \N {"count": 4, "function_name": "generate_ideas", "ideas_created": 4, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:05:24.293661+00 90 21 +660 2026-01-01 00:05:46.726734+00 idea_generation 5 0.0267 gpt-5.1 1700 2454 content_idea \N {"count": 4, "function_name": "generate_ideas", "ideas_created": 4, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:05:46.726774+00 90 21 +661 2026-01-01 00:06:25.709842+00 content_generation 6 0.0326 gpt-5.1 2167 2992 content 296 {"count": 1, "task_id": 296, "content_id": 204, "word_count": 1683, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:06:25.709898+00 90 21 +662 2026-01-01 00:06:52.454338+00 content_generation 6 0.0334 gpt-5.1 2023 3092 content 295 {"count": 1, "task_id": 295, "content_id": 205, "word_count": 1728, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:06:52.454369+00 90 21 +663 2026-01-01 00:07:24.670167+00 content_generation 6 0.0405 gpt-5.1 2054 3796 content 294 {"count": 1, "task_id": 294, "content_id": 206, "word_count": 2114, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:07:24.670226+00 90 21 +664 2026-01-01 00:07:59.700906+00 content_generation 7 0.0428 gpt-5.1 2028 4028 content 293 {"count": 1, "task_id": 293, "content_id": 207, "word_count": 2222, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:07:59.700964+00 90 21 +665 2026-01-01 00:08:32.549853+00 content_generation 6 0.0331 gpt-5.1 2189 3039 content 292 {"count": 1, "task_id": 292, "content_id": 208, "word_count": 1643, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:08:32.549888+00 90 21 +681 2026-01-06 00:11:36.556686+00 image_generation 5 \N dall-e-3 \N \N \N {} 2026-01-06 00:11:36.556735+00 90 21 +701 2026-01-12 11:52:32.654928+00 image_generation 1 0.0090 runware:97@1 \N \N image 533 {"size": "1792x1024", "model": "runware:97@1", "image_id": 533, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:52:32.654971+00 90 21 +698 2026-01-12 11:51:50.19268+00 image_generation 1 0.0090 runware:97@1 \N \N image 529 {"size": "1792x1024", "model": "runware:97@1", "image_id": 529, "provider": "runware", "content_id": 213, "image_type": "featured"} 2026-01-12 11:51:50.192723+00 90 21 +695 2026-01-12 11:51:07.231586+00 image_generation 1 0.0090 runware:97@1 \N \N image 537 {"size": "1024x1024", "model": "runware:97@1", "image_id": 537, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:51:07.231625+00 90 21 +692 2026-01-12 11:50:27.23347+00 image_generation 1 0.0090 runware:97@1 \N \N image 535 {"size": "1024x1024", "model": "runware:97@1", "image_id": 535, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:50:27.233509+00 90 21 +686 2026-01-12 11:48:58.615684+00 image_generation 1 0.0090 runware:97@1 \N \N image 548 {"size": "1792x1024", "model": "runware:97@1", "image_id": 548, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:58.615789+00 90 21 +683 2026-01-12 11:48:20.266192+00 image_generation 1 0.0090 runware:97@1 \N \N image 544 {"size": "1792x1024", "model": "runware:97@1", "image_id": 544, "provider": "runware", "content_id": 216, "image_type": "featured"} 2026-01-12 11:48:20.266239+00 90 21 +689 2026-01-12 11:49:41.90044+00 image_generation 1 0.0090 runware:97@1 \N \N image 541 {"size": "1792x1024", "model": "runware:97@1", "image_id": 541, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:49:41.900499+00 90 21 +666 2026-01-01 00:09:15.287481+00 content_generation 6 0.0415 gpt-5.1 2019 3897 content 291 {"count": 1, "task_id": 291, "content_id": 209, "word_count": 2119, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:09:15.287526+00 90 21 +667 2026-01-01 00:09:48.767527+00 content_generation 6 0.0375 gpt-5.1 2074 3490 content 290 {"count": 1, "task_id": 290, "content_id": 210, "word_count": 1878, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:09:48.767561+00 90 21 +668 2026-01-01 00:10:19.193571+00 content_generation 6 0.0345 gpt-5.1 2068 3188 content 289 {"count": 1, "task_id": 289, "content_id": 211, "word_count": 1670, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:19.193608+00 90 21 +669 2026-01-01 00:10:32.327336+00 image_prompt_extraction 2 0.0078 gpt-5.1 608 708 image \N {"count": 3, "function_name": "generate_image_prompts", "prompts_created": 3, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:32.32738+00 90 21 +670 2026-01-01 00:10:39.734014+00 image_prompt_extraction 2 0.0079 gpt-5.1 596 711 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:39.734045+00 90 21 +671 2026-01-01 00:10:47.059336+00 image_prompt_extraction 2 0.0078 gpt-5.1 590 706 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:47.05938+00 90 21 +672 2026-01-01 00:10:56.613812+00 image_prompt_extraction 2 0.0083 gpt-5.1 606 756 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:10:56.613856+00 90 21 +673 2026-01-01 00:11:05.939718+00 image_prompt_extraction 2 0.0083 gpt-5.1 605 757 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:05.939761+00 90 21 +674 2026-01-01 00:11:13.468777+00 image_prompt_extraction 2 0.0076 gpt-5.1 450 708 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:13.468818+00 90 21 +675 2026-01-01 00:11:21.342646+00 image_prompt_extraction 2 0.0080 gpt-5.1 609 720 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:21.342686+00 90 21 +676 2026-01-01 00:11:29.146994+00 image_prompt_extraction 2 0.0080 gpt-5.1 610 725 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-01 00:11:29.147029+00 90 21 +677 2026-01-03 17:52:05.376517+00 content_generation 6 0.0359 gpt-5.1 2167 3320 content 296 {"count": 1, "task_id": 296, "content_id": 212, "word_count": 1837, "function_name": "generate_content", "clusters_created": 0, "keywords_updated": 0} 2026-01-03 17:52:05.376624+00 90 21 +678 2026-01-03 17:52:34.681152+00 image_prompt_extraction 2 0.0079 gpt-5.1 595 720 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-03 17:52:34.681186+00 90 21 +679 2026-01-03 18:10:26.110037+00 image_prompt_extraction 2 0.0074 gpt-5.1 590 664 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-03 18:10:26.11007+00 90 21 +680 2026-01-03 18:14:28.857052+00 image_prompt_extraction 2 0.0079 gpt-5.1 609 709 image \N {"count": 5, "function_name": "generate_image_prompts", "prompts_created": 5, "clusters_created": 0, "keywords_updated": 0} 2026-01-03 18:14:28.857092+00 90 21 +704 2026-01-12 23:41:43.868395+00 image_generation 1 0.0090 runware:97@1 \N \N image 551 {"size": "1792x1024", "model": "runware:97@1", "image_id": 551, "provider": "runware", "content_id": 217, "image_type": "in_article"} 2026-01-12 23:41:43.868429+00 90 21 +703 2026-01-12 23:41:28.646133+00 image_generation 1 0.0090 runware:97@1 \N \N image 549 {"size": "1792x1024", "model": "runware:97@1", "image_id": 549, "provider": "runware", "content_id": 217, "image_type": "featured"} 2026-01-12 23:41:28.646179+00 90 21 +699 2026-01-12 11:52:03.011292+00 image_generation 1 0.0090 runware:97@1 \N \N image 531 {"size": "1792x1024", "model": "runware:97@1", "image_id": 531, "provider": "runware", "content_id": 213, "image_type": "in_article"} 2026-01-12 11:52:03.01134+00 90 21 +696 2026-01-12 11:51:20.913451+00 image_generation 1 0.0090 runware:97@1 \N \N image 538 {"size": "1792x1024", "model": "runware:97@1", "image_id": 538, "provider": "runware", "content_id": 214, "image_type": "in_article"} 2026-01-12 11:51:20.913496+00 90 21 +693 2026-01-12 11:50:42.182791+00 image_generation 1 0.0090 runware:97@1 \N \N image 534 {"size": "1792x1024", "model": "runware:97@1", "image_id": 534, "provider": "runware", "content_id": 214, "image_type": "featured"} 2026-01-12 11:50:42.182864+00 90 21 +684 2026-01-12 11:48:32.928685+00 image_generation 1 0.0090 runware:97@1 \N \N image 546 {"size": "1792x1024", "model": "runware:97@1", "image_id": 546, "provider": "runware", "content_id": 216, "image_type": "in_article"} 2026-01-12 11:48:32.928764+00 90 21 +687 2026-01-12 11:49:14.444237+00 image_generation 1 0.0090 runware:97@1 \N \N image 540 {"size": "1024x1024", "model": "runware:97@1", "image_id": 540, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:49:14.444279+00 90 21 +690 2026-01-12 11:49:56.782902+00 image_generation 1 0.0090 runware:97@1 \N \N image 542 {"size": "1024x1024", "model": "runware:97@1", "image_id": 542, "provider": "runware", "content_id": 215, "image_type": "in_article"} 2026-01-12 11:49:56.782949+00 90 21 +\. + + +-- +-- Data for Name: igny8_deployment_records; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_deployment_records (id, version, deployed_version, status, deployed_at, deployment_url, error_message, metadata, created_at, updated_at, tenant_id, sector_id, site_id, site_blueprint_id) FROM stdin; +\. + + +-- +-- Data for Name: igny8_email_log; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_email_log (id, message_id, to_email, from_email, subject, template_name, status, provider, error_message, tags, sent_at) FROM stdin; +1 a0389ff5-a758-4192-a99d-98a81fde8737 test-tuzous8th@srv1.mail-tester.com support@igny8.com [TEST] Welcome to IGNY8! welcome sent resend ["test", "auth"] 2026-01-08 09:49:50.242985+00 +2 25ce1364-ce9e-4af9-951e-3f3526b528dc test-nepwlafep@srv1.mail-tester.com support@igny8.com [TEST] Welcome to IGNY8! welcome sent resend ["test", "auth"] 2026-01-08 10:03:06.265575+00 +3 07e36c1c-b6d0-4cd7-a95a-4bccf7b4d342 tacbit.com@gmail.com support@igny8.com [TEST] Welcome to IGNY8! welcome sent resend ["test", "auth"] 2026-01-08 10:05:12.405064+00 +4 3c5cb115-898d-47c1-a6df-3da3344efeff test-7uywffsfo@srv1.mail-tester.com support@igny8.com [TEST] Welcome to IGNY8! welcome sent resend ["test", "auth"] 2026-01-08 10:28:42.522402+00 +5 a43ba8be-9552-434c-88fa-a7a55f51b69b test-r7ia9xqs5@srv1.mail-tester.com support@igny8.com [TEST] Welcome to IGNY8! welcome sent resend ["test", "auth"] 2026-01-08 11:54:16.997143+00 +6 c7c92cea-93a8-4d8b-a1b0-b51ce33f5bc1 bluesalman@hotmail.com support@igny8.com [TEST] Subscription Renewal Reminder subscription_renewal sent resend ["test", "billing"] 2026-01-12 13:43:50.796824+00 +\. + + +-- +-- Data for Name: igny8_email_settings; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_email_settings (id, from_email, from_name, reply_to_email, company_name, company_address, logo_url, support_email, support_url, unsubscribe_url, send_welcome_emails, send_billing_emails, send_subscription_emails, send_low_credit_warnings, low_credit_threshold, renewal_reminder_days, updated_at, updated_by_id, email_provider, smtp_host, smtp_password, smtp_port, smtp_timeout, smtp_use_ssl, smtp_use_tls, smtp_username) FROM stdin; +1 support@igny8.com IGNY8 support@igny8.com IGNY8 AI SEO 17595 Harvard Ave, Suite C,\r\nIrvine, California, 92614 https://app.igny8.com/images/logo/IGNY8_DARK_LOGO.png support@igny8.com https://igny8.com/contact https://app.igny8.com/account/settings t t t t 100 7 2026-01-08 05:20:08.295364+00 3 resend 587 30 f t +\. + + +-- +-- Data for Name: igny8_email_templates; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_email_templates (id, template_name, template_path, display_name, description, template_type, default_subject, required_context, sample_context, is_active, send_count, last_sent_at, created_at, updated_at) FROM stdin; +2 password_reset emails/password_reset.html Password Reset Sent when user requests password reset auth Reset Your IGNY8 Password ["user_name", "reset_url", "frontend_url"] {"reset_url": "https://app.igny8.com/reset?token=abc123", "user_name": "John", "frontend_url": "https://app.igny8.com"} t 0 \N 2026-01-08 01:24:08.786441+00 2026-01-08 01:24:08.786449+00 +3 email_verification emails/email_verification.html Email Verification Sent to verify user email address auth Verify Your IGNY8 Email ["user_name", "verification_url", "frontend_url"] {"user_name": "John", "frontend_url": "https://app.igny8.com", "verification_url": "https://app.igny8.com/verify?token=abc123"} t 0 \N 2026-01-08 01:24:08.788642+00 2026-01-08 01:24:08.78865+00 +4 payment_confirmation emails/payment_confirmation.html Payment Confirmation Sent when user submits payment for approval billing Payment Confirmation Received ["account_name", "invoice_number", "amount", "currency", "payment_method"] {"amount": "99.00", "currency": "USD", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "invoice_number": "INV-001", "payment_method": "Bank Transfer", "manual_reference": "REF123"} t 0 \N 2026-01-08 01:24:08.790751+00 2026-01-08 01:24:08.790758+00 +5 payment_approved emails/payment_approved.html Payment Approved Sent when payment is approved and account activated billing Payment Approved - Account Activated ["account_name", "invoice_number", "amount", "plan_name"] {"amount": "99.00", "currency": "USD", "plan_name": "Starter Plan", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "dashboard_url": "https://app.igny8.com/dashboard", "invoice_number": "INV-001"} t 0 \N 2026-01-08 01:24:08.792752+00 2026-01-08 01:24:08.792757+00 +6 payment_rejected emails/payment_rejected.html Payment Rejected Sent when payment is rejected/declined billing Payment Declined - Action Required ["account_name", "invoice_number", "reason"] {"amount": "99.00", "reason": "Invalid reference number", "currency": "USD", "billing_url": "https://app.igny8.com/account/billing", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "invoice_number": "INV-001"} t 0 \N 2026-01-08 01:24:08.795233+00 2026-01-08 01:24:08.795243+00 +7 payment_failed emails/payment_failed.html Payment Failed Sent when automatic payment fails billing Payment Failed - Action Required ["account_name", "plan_name", "failure_reason"] {"plan_name": "Starter Plan", "billing_url": "https://app.igny8.com/account/plans", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "failure_reason": "Card declined"} t 0 \N 2026-01-08 01:24:08.797531+00 2026-01-08 01:24:08.797537+00 +8 subscription_activated emails/subscription_activated.html Subscription Activated Sent when subscription is activated billing Subscription Activated ["account_name", "plan_name", "included_credits"] {"plan_name": "Starter Plan", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "dashboard_url": "https://app.igny8.com/dashboard", "included_credits": 5000} t 0 \N 2026-01-08 01:24:08.80002+00 2026-01-08 01:24:08.800032+00 +10 refund_notification emails/refund_notification.html Refund Notification Sent when refund is processed billing Refund Processed ["user_name", "invoice_number", "refund_amount", "reason"] {"reason": "Customer request", "currency": "USD", "user_name": "John", "frontend_url": "https://app.igny8.com", "refund_amount": "99.00", "invoice_number": "INV-001", "original_amount": "99.00"} t 0 \N 2026-01-08 01:24:08.80495+00 2026-01-08 01:24:08.804958+00 +11 low_credits emails/low_credits.html Low Credits Warning Sent when account credits fall below threshold notification Low Credits Warning - IGNY8 ["account_name", "current_credits", "threshold"] {"threshold": 100, "topup_url": "https://app.igny8.com/account/usage", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "current_credits": 50} t 0 \N 2026-01-08 01:24:08.807352+00 2026-01-08 01:24:08.80736+00 +9 subscription_renewal emails/subscription_renewal.html Subscription Renewal Reminder Sent before subscription renewal billing Subscription Renewal Reminder ["account_name", "plan_name", "renewal_date", "days_until_renewal"] {"amount": "99.00", "currency": "USD", "plan_name": "Starter Plan", "account_name": "Test Account", "frontend_url": "https://app.igny8.com", "renewal_date": "2026-01-15", "subscription_url": "https://app.igny8.com/account/plans", "days_until_renewal": 7} t 1 2026-01-12 13:43:50.793686+00 2026-01-08 01:24:08.80268+00 2026-01-08 01:24:08.80269+00 +1 welcome emails/welcome.html Welcome Email Sent to new users after registration auth Welcome to IGNY8! ["user_name", "account_name", "login_url", "frontend_url"] {"login_url": "https://app.igny8.com/login", "user_name": "John", "account_name": "Test Account", "frontend_url": "https://app.igny8.com"} t 5 2026-01-08 11:54:16.986286+00 2026-01-08 01:24:08.783368+00 2026-01-08 01:24:08.783377+00 +\. + + +-- +-- Data for Name: igny8_global_ai_prompts; Type: TABLE DATA; Schema: public; Owner: igny8 +-- + +COPY public.igny8_global_ai_prompts (id, prompt_type, prompt_value, description, variables, is_active, version, last_updated, created_at) FROM stdin; +1 clustering # Semantic Authority Grid - Keyword Clustering Engine\r\n\r\nYou are a semantic strategist building **topic ecosystems** using the Semantic Authority Grid methodology. Analyze keywords and group them into **mini-ecosystems** where each cluster represents a complete, self-contained topic authority area.\r\n\r\n---\r\n\r\n## CORE PRINCIPLE\r\n\r\n**Clusters are Mini-Ecosystems**\r\n\r\nEach cluster must function as a complete topical authority unit containing:\r\n- 1 root/anchor topic (hub page potential)\r\n- Supporting semantic variations (blog topics)\r\n- Natural attribute dimensions (filters/taxonomies)\r\n- Clear user journey pathways\r\n- ignore volume & difficulty provided\r\n---\r\n\r\n## Keywords to Cluster\r\n\r\n{\r\n "keywords": [IGNY8_KEYWORDS]\r\n}\r\n\r\n---\r\n\r\n## OUTPUT FORMAT JSON Object\r\n\r\nReturn ONLY valid JSON with no explanations or commentary:\r\n\r\n{\r\n "clusters": [\r\n {\r\n "name": "...",\r\n "description": "...",\r\n "keywords": ["...", "...", "..."]\r\n }\r\n ]\r\n}\r\n\r\n---\r\n\r\n## CLUSTERING METHODOLOGY\r\n\r\n### Step 1: Identify Natural Semantic Overlaps\r\n\r\nKeywords cluster together when they share **2+ dimensional intersections**:\r\n\r\n**Dimensional Framework:**\r\n1. **Topic/Subject** → The core thing being discussed\r\n2. **Problem/Need** → What pain point or desire drives the search\r\n3. **Solution/Method** → How something is done or achieved\r\n4. **Feature/Attribute** → Specific characteristics (heated, waterproof, organic, portable)\r\n5. **Persona/Audience** → Who needs this (beginners, professionals, age groups, conditions)\r\n6. **Use-Case/Application** → Where/when/why it's used (pregnancy, travel, office, summer)\r\n7. **Product Type/Format** → Category or form factor (serum, massager, organizer, sheets)\r\n8. **Comparison/Alternative** → Explicit or implied comparison queries\r\n9. **Context/Modifier** → Geographic, temporal, or situational qualifiers\r\n\r\n**Example of Multi-Dimensional Intersection:**\r\n- "best foot massagers for plantar fasciitis"\r\n - Dimensions: Product Type (foot massager) + Persona (plantar fasciitis sufferers) + Problem (pain relief)\r\n- "heated shiatsu back massager"\r\n - Dimensions: Product Type (back massager) + Feature (heated) + Feature (shiatsu)\r\n- "are back massagers safe during pregnancy"\r\n - Dimensions: Product Type (back massager) + Persona (pregnant women) + Problem (safety concern)\r\n\r\nThese belong in the same cluster because they share **Product Type dimension** and serve **interconnected user journeys**.\r\n\r\n---\r\n\r\n### Step 2: Map User Journey Patterns\r\n\r\nEach cluster should support natural user exploration paths:\r\n\r\n**Journey Patterns:**\r\n- **Problem → Information → Solution → Product**\r\n - "what causes plantar fasciitis" → "best treatments for plantar fasciitis" → "foot massagers for plantar fasciitis"\r\n \r\n- **General → Specific → Variant**\r\n - "foot massagers" → "shiatsu foot massagers" → "heated shiatsu foot massagers"\r\n \r\n- **Question → Explanation → Options → Decision**\r\n - "do vitamin c serums work" → "how vitamin c brightens skin" → "best vitamin c serums" → "vitamin c vs retinol"\r\n \r\n- **Feature Discovery → Feature Comparison → Feature + Use-Case**\r\n - "heated massagers" → "heated vs unheated massagers" → "heated massagers for arthritis"\r\n\r\n**Cluster Test:** Can a user naturally move from any keyword in the cluster to any other keyword in a logical research/buying journey?\r\n\r\n---\r\n\r\n### Step 3: Identify Cluster Anchors (Hub Potential)\r\n\r\nEach cluster needs a clear **root keyword** that can serve as the hub page:\r\n\r\n**Hub Characteristics:**\r\n- Broad enough to encompass all cluster keywords\r\n- Specific enough to be commercially or informationally valuable\r\n- Natural landing point for user journey\r\n- Can support 3-10+ supporting content pieces\r\n\r\n**Examples:**\r\n- ✅ "foot massagers for plantar fasciitis" (hub)\r\n - Supports: safety concerns, specific features, comparisons, use-cases\r\n- ✅ "vitamin c serums for brightening" (hub)\r\n - Supports: vs alternatives, for skin types, application guides, safety\r\n- ❌ "massagers" (too broad, no natural ecosystem)\r\n- ❌ "renpho foot massager cord length" (too narrow, can't support ecosystem)\r\n\r\n---\r\n\r\n### Step 4: Extract Attribute Dimensions\r\n\r\nFor each cluster, identify **taxonomy-worthy dimensions** that could become:\r\n- Product filters\r\n- Content tags\r\n- URL parameters\r\n- Faceted navigation\r\n\r\n**Extraction Method:**\r\nLook for recurring **modifiers** across cluster keywords:\r\n- Material: cotton, linen, silk, organic\r\n- Feature: heated, waterproof, portable, wireless\r\n- Size: king, queen, twin, compact\r\n- Type: shiatsu, percussion, vibration, EMS\r\n- Audience: men, women, seniors, athletes\r\n- Context: travel, home, office, car\r\n\r\nThese dimensions should appear in the cluster's description as **natural grouping signals**.\r\n\r\n---\r\n\r\n## CLUSTERING RULES\r\n\r\n### Formation Rules\r\n\r\n1. **Semantic Coherence**\r\n - Keywords must share meaningful semantic relationships\r\n - Shared words alone don't make a cluster\r\n - "back massager" + "back support pillow" = different clusters (different product ecosystems)\r\n\r\n2. **Dimensional Intersection**\r\n - Minimum 2 shared dimensions required\r\n - More intersections = stronger cluster\r\n - Keywords with 3+ shared dimensions are core cluster members\r\n\r\n3. **User Journey Viability**\r\n - Cluster keywords should form natural navigation paths\r\n - Hub → supporting blogs → variants should flow logically\r\n - Test: Would a user exploring this topic naturally encounter all these keywords?\r\n\r\n4. **Ecosystem Completeness**\r\n - Each cluster should be self-contained\r\n - Should support 1 hub page + 3-10 supporting articles\r\n - Must have enough depth for authority building\r\n\r\n5. **Exclusion Over Inclusion**\r\n - Better to leave keywords unclustered than force weak groupings\r\n - Outliers should be excluded from output\r\n - Only cluster keywords with strong semantic bonds\r\n\r\n### Size & Quality Rules\r\n\r\n- **Minimum cluster size:** 3 keywords (otherwise it's not an ecosystem)\r\n- **Maximum cluster size:** 15 keywords (beyond this, consider splitting)\r\n- **Optimal cluster size:** 5-10 keywords (hub + supporting + variants)\r\n- **No keyword duplication:** Each keyword appears in exactly one cluster\r\n- **Quality over quantity:** 5 strong clusters > 15 weak clusters\r\n\r\n---\r\n\r\n## OUTPUT STRUCTURE json object\r\n{\r\n "clusters": [\r\n {\r\n "name": "[Natural, SEO-relevant cluster name representing the root topic]",\r\n "description": "[2-3 sentences explaining: (1) what semantic dimensions bind these keywords, (2) what user journey or problem space this cluster addresses]",\r\n "keywords": ["keyword 1", "keyword 2", "keyword 3", "..."]\r\n }\r\n ]\r\n}\r\n\r\n\r\n### Naming Guidelines\r\n\r\n**Good Cluster Names:**\r\n- "Foot Massagers for Plantar Fasciitis Treatment"\r\n- "Organic Cotton Bedding for Summer"\r\n- "Vitamin C Serums for Skin Brightening"\r\n- "Waterproof Car Trunk Organizers"\r\n\r\n**Poor Cluster Names:**\r\n- "Massagers" (too broad)\r\n- "Products" (meaningless)\r\n- "Heated" (just a feature, not a topic)\r\n- "Queries about safety" (meta, not topical)\r\n\r\n### Description Guidelines\r\n\r\n**Good Description Format:**\r\n"This cluster covers [TOPIC] focused on [PROBLEM/USE-CASE] for [AUDIENCE]. Keywords share dimensions of [DIMENSION 1] and [DIMENSION 2], forming a natural ecosystem for users researching [USER JOURNEY]."\r\n\r\n**Example:**\r\n"This cluster covers foot massage devices specifically for plantar fasciitis relief. Keywords share dimensions of product type (foot massager), health condition (plantar fasciitis), and therapeutic features (shiatsu, heated, EMS), forming a natural ecosystem for users researching pain relief solutions and comparing treatment options."\r\n\r\n---\r\n\r\n## VALIDATION CHECKLIST\r\n\r\nBefore finalizing clusters, verify:\r\n\r\n✓ **Hub Potential:** Each cluster has 1 clear anchor keyword for hub page\r\n✓ **Dimensional Overlap:** Keywords share 2+ semantic dimensions\r\n✓ **User Journey:** Natural navigation paths exist within cluster\r\n✓ **Attribute Dimensions:** Recurring modifiers can become filters/taxonomies\r\n✓ **Ecosystem Completeness:** Supports 1 hub + 3-10 supporting articles\r\n✓ **Semantic Coherence:** Keywords genuinely belong together, not just word overlap\r\n✓ **No Duplication:** Each keyword appears once\r\n✓ **No Weak Clusters:** Only include strong, viable ecosystems\r\n\r\n---\r\n\r\n## ANTI-PATTERNS TO AVOID\r\n\r\n❌ **Single-word grouping:** "All keywords with 'heated' go together"\r\n❌ **Forced categorization:** "Must fit everything into clusters"\r\n❌ **Shallow connections:** Keywords share one word but no semantic relationship\r\n❌ **Traditional SEO intent labels:** Don't use "informational" or "commercial" as clustering logic\r\n❌ **Assumed site structure:** Don't cluster based on existing categories\r\n❌ **Word-matching only:** "massage" keywords together regardless of context\r\n\r\n✅ **Multi-dimensional analysis:** Find deep semantic intersections\r\n✅ **User journey modeling:** Natural exploration and research paths\r\n✅ **Ecosystem thinking:** Self-contained topical authority units\r\n✅ **Attribute extraction:** Recurring dimensions that create taxonomies\r\n✅ **Quality filtering:** Only strong, viable clusters in output\r\n\r\n---\r\n\r\n## EXAMPLES\r\n\r\n### Example 1: Strong Cluster\r\n\r\n{\r\n "name": "Heated Shiatsu Back Massagers for Pain Relief",\r\n "description": "This cluster covers back massage devices combining heated therapy with shiatsu technique for pain management. Keywords intersect on product type (back massager), features (heated, shiatsu), and problem-solving (pain relief, muscle tension), supporting a complete user journey from problem awareness to product comparison.",\r\n "keywords": [\r\n "heated back massagers",\r\n "shiatsu back massager with heat",\r\n "best heated massagers for back pain",\r\n "do heated massagers help muscle pain",\r\n "heated vs unheated back massagers",\r\n "shiatsu massage for lower back pain"\r\n ]\r\n}\r\n\r\n\r\n### Example 2: Multi-Dimensional Cluster\r\n\r\n{\r\n "name": "Organic Cotton Bed Sheets for Hot Sleepers",\r\n "description": "This cluster addresses cooling bedding solutions using organic materials. Keywords intersect on material (organic cotton), product type (bed sheets), use-case (hot weather/hot sleepers), and benefit (breathability, cooling), forming an ecosystem for users researching temperature-regulating sleep solutions.",\r\n "keywords": [\r\n "organic cotton sheets for summer",\r\n "best cooling cotton bed sheets",\r\n "breathable organic cotton bedding",\r\n "cotton vs linen sheets for hot sleepers",\r\n "organic cotton king size sheets",\r\n "are cotton sheets good for hot weather"\r\n ]\r\n}\r\n\r\n\r\n### Example 3: What NOT to Cluster\r\n\r\n❌ BAD CLUSTER:\r\n{\r\n "name": "Massager Products",\r\n "keywords": [\r\n "foot massagers",\r\n "back massagers", \r\n "neck massagers",\r\n "massage oils",\r\n "massage chairs"\r\n ]\r\n}\r\n\r\n**Why it's bad:** Keywords share only the word "massage" but represent completely different product ecosystems with different user journeys, problems, and attributes. These should be separate clusters. {"clusters": [{"name": "...", "keywords": ["...", "...", "..."], "description": "..."}]} t 1 2025-12-20 19:25:26.256456+00 2025-12-20 12:53:51.902206+00 +2 ideas # SEO Content Idea Generator\r\n\r\nYou are a content strategist. Generate content ideas and simple outlines for keyword clusters. The actual content will be written by a separate system.\r\n\r\n---\r\n\r\n## INPUT FORMAT\r\n\r\n**Clusters to analyze:**\r\n[IGNY8_CLUSTERS]\r\n\r\n**Keywords in each cluster:**\r\n[IGNY8_CLUSTER_KEYWORDS]\r\n\r\n---\r\n\r\n## OUTPUT REQUIREMENTS\r\n\r\nGenerate exactly **4 content ideas per cluster**:\r\n- 1 cluster hub page (comprehensive overview)\r\n- 3 supporting articles (focused on specific angles)\r\n\r\n---\r\n\r\n## OUTPUT JSON OBJECT STRUCTURE\r\n\r\n\r\n## OUTPUT JSON OBJECT STRUCTURE\r\n{\r\n "ideas": [\r\n {\r\n "title": "[Compelling title with primary keyword]",\r\n "description": {\r\n "overview": "[2-3 sentence description of what this content covers and its unique angle]",\r\n "outline": {\r\n "intro_focus": "[What the introduction should establish]",\r\n "main_sections": [\r\n {\r\n "h2_topic": "[Section topic/angle]",\r\n "coverage": "[What this section should cover - 1 sentence]"\r\n }\r\n ]\r\n }\r\n },\r\n "covered_keywords": "[3-8 relevant keywords from cluster, comma-separated]",\r\n "content_type": "post|page",\r\n "content_structure": "cluster_hub|guide_tutorial|how_to|comparison|review|top_listicle|question",\r\n "cluster_id": "[Cluster ID number]",\r\n "estimated_word_count": 1500\r\n }\r\n ]\r\n}\r\n\r\n---\r\n\r\n## CONTENT STRUCTURE BY TYPE\r\n\r\n### Cluster Hub (1 per cluster)\r\n- **Purpose**: Comprehensive overview of entire topic cluster\r\n- **Sections**: 8-10 H2 sections\r\n- **Coverage**: Introduces all major subtopics, links to supporting content\r\n- **Keywords**: Covers 5-8 keywords from cluster naturally\r\n\r\n### Guide/Tutorial (Supporting)\r\n- **Purpose**: Step-by-step educational content\r\n- **Sections**: 6-8 H2 sections\r\n- **Coverage**: Process-oriented, actionable steps\r\n- **Keywords**: Focuses on 2-4 specific keywords\r\n\r\n### How-To (Supporting)\r\n- **Purpose**: Solve a specific problem\r\n- **Sections**: 5-7 H2 sections\r\n- **Coverage**: Problem → solution framework\r\n- **Keywords**: Long-tail, question-based keywords\r\n\r\n### Comparison (Supporting)\r\n- **Purpose**: Compare options/alternatives\r\n- **Sections**: 6-8 H2 sections\r\n- **Coverage**: Feature analysis, pros/cons, recommendations\r\n- **Keywords**: "vs", "best", "alternative" keywords\r\n\r\n### Review (Supporting)\r\n- **Purpose**: Evaluate specific products/services\r\n- **Sections**: 6-8 H2 sections\r\n- **Coverage**: Features, testing, verdict\r\n- **Keywords**: Product/brand names + descriptive terms\r\n\r\n### Top Listicle (Supporting)\r\n- **Purpose**: Curated ranked list\r\n- **Sections**: 6-8 H2 sections (intro + items + conclusion)\r\n- **Coverage**: Criteria, ranked items, selection guide\r\n- **Keywords**: "best", "top", number-based keywords\r\n\r\n### Question (Supporting)\r\n- **Purpose**: Answer specific query\r\n- **Sections**: 5-7 H2 sections\r\n- **Coverage**: Question → context → answer → implications\r\n- **Keywords**: Question keywords, related queries\r\n\r\n---\r\n\r\n## OUTLINE REQUIREMENTS\r\n\r\nFor each idea, provide:\r\n\r\n1. **Intro Focus**: What angle/hook the introduction should take (1 sentence)\r\n\r\n2. **Main Sections**: 5-10 H2 topics with brief coverage notes\r\n - List the H2 section topics only\r\n - 1 sentence on what each section should cover\r\n - No need for H3 breakdown (content generator will handle)\r\n - No formatting details (content generator will handle)\r\n\r\n3. **Section suggestions**:\r\n - Foundation/basics sections (if needed)\r\n - Core concept sections (main body)\r\n - Application/implementation sections\r\n - Advanced/future sections (if appropriate)\r\n\r\n---\r\n\r\n## TITLE GUIDELINES\r\n\r\n- Naturally include primary keyword\r\n- 50-65 characters ideal\r\n- Compelling and specific\r\n- Match content structure type:\r\n - Hub: "Complete Guide to [Topic]"\r\n - How-to: "How to [Action] [Object/Goal]"\r\n - Comparison: "[X] vs [Y]: Which Is Better?"\r\n - Review: "[Product/Service] Review: [Key Benefit]"\r\n - Listicle: "[Number] Best [Items] for [Purpose]"\r\n - Question: "[Question Using Primary Keyword]?"\r\n\r\n---\r\n\r\n## KEYWORD COVERAGE\r\n\r\n### Covered Keywords (5-8 per idea)\r\nList all relevant keywords from the cluster that this content will naturally address:\r\n- Include the main keyword that appears in the title\r\n- Include related search terms from cluster\r\n- Include natural variations and long-tail keywords\r\n- Include supporting/related terms\r\n\r\n**The content generator will automatically:**\r\n- Identify the primary keyword from title and context\r\n- Categorize remaining keywords as secondary\r\n- Optimize keyword placement and density\r\n\r\n### Coverage Strategy\r\n- **Cluster Hub**: 6-10 keywords (comprehensive coverage)\r\n- **Supporting Content**: 4-6 keywords (focused coverage)\r\n\r\n---\r\n\r\n## CONTENT ANGLE REQUIREMENTS\r\n\r\nEach idea must have a **unique angle**:\r\n\r\n✓ Different content structure types across the 4 ideas\r\n✓ Different target intents (informational, commercial, navigational)\r\n✓ Different depth levels (overview vs deep-dive)\r\n✓ No duplicate section topics across ideas in same cluster\r\n\r\n**Example for "Email Marketing" cluster:**\r\n1. Hub: "Complete Guide to Email Marketing" \r\n - covered_keywords: "email marketing, email campaigns, newsletter marketing, email automation, email list building, email marketing strategy"\r\n2. Supporting: "How to Build an Email List from Scratch"\r\n - covered_keywords: "email list building, grow email list, subscriber acquisition, lead magnets, email signup forms"\r\n3. Supporting: "Mailchimp vs ConvertKit: Email Platform Comparison"\r\n - covered_keywords: "mailchimp vs convertkit, email marketing platforms, email software comparison, best email tools"\r\n4. Supporting: "10 Best Email Marketing Tools for Small Businesses"\r\n - covered_keywords: "email marketing tools, email software, email marketing platforms, small business email marketing"\r\n\r\n---\r\n\r\n## QUALITY CHECKS\r\n\r\nBefore finalizing, verify:\r\n- ✓ 4 ideas per cluster (1 hub + 3 supporting)\r\n- ✓ Each idea has unique content_structure type\r\n- ✓ Title includes a relevant keyword from cluster\r\n- ✓ 5-10 H2 sections outlined per idea\r\n- ✓ 5-8 covered_keywords listed per idea\r\n- ✓ Content angles don't overlap\r\n- ✓ Valid JSON format\r\n\r\n---\r\n\r\n## OUTPUT FORMAT\r\n\r\nReturn ONLY valid JSON with no comments or explanations.\r\n\r\nThe "outline" object should be simple and high-level - just enough to guide the content generator. The actual detailed structure, word counts, formatting, H3 subsections, lists, tables, etc. will be handled by the content generation system.\r\n\r\n---\r\n\r\n## WHAT NOT TO INCLUDE\r\n\r\n❌ Detailed H3 subsections (content generator handles this)\r\n❌ Specific formatting instructions (paragraph/list/table details)\r\n❌ Word count per section (content generator calculates)\r\n❌ Detailed content descriptions (keep coverage notes brief)\r\n❌ HTML structure (content generator outputs HTML)\r\n❌ Introduction hook text (content generator writes this)\r\n\r\nKeep outlines strategic and high-level. Let the content generation system handle tactical execution. {"ideas": [{"title": "[Compelling title with primary keyword]", "cluster_id": "[Cluster ID number]", "description": {"outline": {"intro_focus": "[What the introduction should establish]", "main_sections": [{"coverage": "[What this section should cover - 1 sentence]", "h2_topic": "[Section topic/angle]"}]}, "overview": "[2-3 sentence description of what this content covers and its unique angle]"}, "content_type": "post|page", "covered_keywords": "[3-8 relevant keywords from cluster, comma-separated]", "content_structure": "cluster_hub|guide_tutorial|how_to|comparison|review|top_listicle|question", "estimated_word_count": 1500}]} t 1 2025-12-20 19:27:56.816041+00 2025-12-20 12:53:51.905161+00 +5 image_prompt_template Image Type: {image_type},\r\nImage Prompt: {image_prompt} [] t 1 2025-12-20 19:57:17.579009+00 2025-12-20 12:53:51.910653+00 +6 negative_prompt text, watermark, logo, signature, username, artist name, blurry, low quality, pixelated, distorted, deformed, duplicate, cropped, out of frame, bad anatomy, bad proportions, extra limbs, missing limbs, floating limbs, disconnected limbs, mutation, mutated, ugly, disgusting, amputation, cartoon, anime [] t 1 2025-12-20 19:57:43.034529+00 2025-12-20 12:53:51.912684+00 +8 product_generation Create comprehensive product content for:\r\n\r\nProduct Name: {product_name}\r\nCategory: {category}\r\nFeatures: {features}\r\nTarget Audience: {audience}\r\n\r\nGenerate:\r\n1. Compelling product description (200-300 words)\r\n2. Key features and benefits (bullet points)\r\n3. Technical specifications\r\n4. Use cases or applications\r\n5. SEO-optimized meta description\r\n\r\nReturn structured JSON with all elements. [] f 1 2025-12-20 19:58:14.244216+00 2025-12-20 12:53:51.917325+00 +9 service_generation Create detailed service page content for:\r\n\r\nService Name: {service_name}\r\nCategory: {category}\r\nKey Benefits: {benefits}\r\nTarget Audience: {audience}\r\n\r\nGenerate:\r\n1. Overview section (150-200 words)\r\n2. Process or methodology (step-by-step)\r\n3. Benefits and outcomes\r\n4. Why choose us / differentiators\r\n5. FAQ section (5-7 questions)\r\n6. Call-to-action suggestions\r\n\r\nReturn structured HTML content. [] f 1 2025-12-20 19:58:23.805071+00 2025-12-20 12:53:51.919307+00 +7 site_structure_generation Design a comprehensive site structure for:\r\n\r\nBusiness Type: {business_type}\r\nPrimary Keywords: {keywords}\r\nTarget Audience: {audience}\r\nGoals: {goals}\r\n\r\nInstructions:\r\n1. Create a logical, user-friendly navigation hierarchy\r\n2. Include essential pages (Home, About, Services/Products, Contact)\r\n3. Design category pages for primary keywords\r\n4. Plan supporting content pages\r\n5. Consider user journey and conversion paths\r\n\r\nReturn a JSON structure:\r\n{\r\n "navigation": [\r\n {\r\n "page": "Page name",\r\n "slug": "url-slug",\r\n "type": "home|category|product|service|content|utility",\r\n "children": []\r\n }\r\n ]\r\n} [] f 1 2025-12-20 19:58:33.034858+00 2025-12-20 12:53:51.914768+00 +10 taxonomy_generation Create a logical taxonomy structure for:\r\n\r\nContent Type: {content_type}\r\nDomain: {domain}\r\nExisting Keywords: {keywords}\r\n\r\nInstructions:\r\n1. Design parent categories that organize content logically\r\n2. Create subcategories for detailed organization\r\n3. Ensure balanced hierarchy (not too deep or flat)\r\n4. Use clear, descriptive category names\r\n5. Consider SEO and user navigation\r\n\r\nReturn a JSON structure:\r\n{\r\n "categories": [\r\n {\r\n "name": "Category Name",\r\n "slug": "category-slug",\r\n "description": "Brief description",\r\n "subcategories": []\r\n }\r\n ]\r\n} [] f 1 2025-12-20 19:58:48.350291+00 2025-12-20 12:53:51.922882+00 +3 content_generation # Editorial Content Generator\r\n\r\nGenerate complete, SEO-optimized HTML content from the provided outline.\r\n\r\n---\r\n\r\n## INPUT\r\n\r\n**CONTENT IDEA:**\r\n[IGNY8_IDEA]\r\n\r\n**KEYWORD CLUSTER:**\r\n[IGNY8_CLUSTER]\r\n\r\n**KEYWORDS:**\r\n[IGNY8_KEYWORDS]\r\n\r\n---\r\n\r\n## OUTPUT FORMAT\r\n\r\n\r\n{\r\n "title": "string",\r\n "meta_title": "string (max 60 chars)",\r\n "meta_description": "string (max 160 chars)",\r\n "content": "string (HTML)",\r\n "word_count": integer,\r\n "primary_keyword": "string",\r\n "secondary_keywords": ["string"],\r\n "tags": ["string"],\r\n "categories": ["Parent > Child"]\r\n}\r\n\r\n\r\n---\r\n\r\n## CRITICAL REQUIREMENTS\r\n\r\n### 1. WORD COUNT: 1000-1200 words target\r\n\r\n**Section breakdown:**\r\n- Introduction: 100-150 words\r\n- Each H2 section: 150-180 words\r\n- Write 6 H2 sections\r\n\r\n### 2. SECTION FORMAT VARIETY\r\n\r\n**For 6 H2 sections, distribute as:**\r\n- 2 sections: Paragraphs ONLY \r\n- 2 section: Paragraphs + Lists\r\n- 1 section: Paragraphs + Tables\r\n* Use block quote element in different sections randomly in sections where table is not used, and make sure to provide the most insightful information within block quote\r\n\r\n**Rules:**\r\n- Randomize which sections get which format\r\n- Never use same pattern for consecutive sections\r\n- Maximum 2 sections can have lists\r\n- Maximum 2 sections can have tables\r\n- Lists: 4-5 items, 15-20 words each\r\n- Tables: 4-5 columns, 5-6 rows with real data\r\n\r\n### 3. CONTENT DEPTH (NOT surface explanations)\r\n\r\n**Every paragraph must:**\r\n- Be 50-60 words \r\n- Explain HOW/WHY, not just WHAT\r\n- Include specific numbers, examples, mechanisms\r\n- Provide actionable insights\r\n\r\n**Lists must contain:**\r\n- 4-5 items maximum\r\n- Each item: 15-20 words\r\n- Specific details with real examples\r\n- Technical specifics (measurements, ranges, capabilities)\r\n\r\n**Tables must contain:**\r\n- 4-5 columns\r\n- 5-6 rows \r\n- Real comparative data (prices, specs, measurements)\r\n- No vague terms (avoid "good", "quality", "effective")\r\n\r\n---\r\n\r\n## HTML STRUCTURE\r\n\r\n### Introduction Format:\r\n\r\n[Hook: 40-50 words]
\r\n[Paragraph 1: 50-70 words with primary keyword]
\r\n[Paragraph 2: 50-60 words]
\r\n[Paragraph 3: 50-60 words]
\r\n\r\n\r\n### H2 Section Format:\r\n\r\n[Opening: 50-70 words explaining core concept]
\r\n\r\n\r\n\r\n\r\n[60-70 words of detailed explanation]
\r\n[60-70 words of detailed explanation]
\r\n\r\n\r\n[50-70 words introducing the list]
\r\n[50-70 words introducing the table]
\r\n| Col1 | Col2 | Col3 | Col4 |
|---|---|---|---|
| Data | Data | Data | Data |
[Closing: 50-70 words synthesizing the section]
\r\n\r\n\r\n**Valid HTML tags only:** ``, ``, ``, ` Your one-stop shop for everything home and garden. Discover a wide range of quality products designed to enhance your living space. [Hook: 40-50 words] [Paragraph 1: 50-70 words with primary keyword] [Paragraph 2: 50-60 words] [Paragraph 3: 50-60 words] [Opening: 50-70 words explaining core concept] [60-70 words of detailed explanation] [60-70 words of detailed explanation] [50-70 words introducing the list] [50-70 words introducing the table] [Closing: 50-70 words synthesizing the section] `, ``, ``, ``, `
`, `
`, ``, ``, `
`, ` `, ` `\r\n\r\n---\r\n\r\n## WRITING RULES\r\n\r\n### DO:\r\n✓ Use specific examples, brands, models, numbers\r\n✓ Explain mechanisms and technical details\r\n✓ Include real data (prices, percentages, measurements)\r\n✓ Write naturally with varied sentence length\r\n✓ Use active voice\r\n✓ Connect ideas logically between paragraphs\r\n\r\n### DON'T:\r\n✗ Generic openings ("In today's world...")\r\n✗ Repeat H2/H3 in first sentence\r\n✗ Robotic transitions ("First...", "Second...")\r\n✗ Filler phrases ("It's important to note...")\r\n✗ Placeholder content ("Brand A", "Model X", "Data 1")\r\n✗ Paragraphs under 40 words or over 80 words\r\n✗ Lists with more than 6 items or items over 25 words\r\n✗ Tables with more than 5 columns or 6 rows\r\n✗ Writing more than 1200 words total\r\n\r\n---\r\n\r\n## KEYWORD USAGE\r\n\r\n**Primary keyword** (identify from title):\r\n- Use in title, intro, meta title/description\r\n- Include in 2-3 H2 headings naturally\r\n- Mention 2-3 times in content (0.5-1% density)\r\n\r\n**Secondary keywords** (3-4 from keyword list):\r\n- Distribute across H2 sections\r\n- Use in H2/H3 headings where natural\r\n- 2-3 mentions each (0.3-0.6% density)\r\n- Include variations and related terms\r\n\r\n---\r\n\r\n## METADATA\r\n\r\n**Meta Title:** Under 60 chars, primary keyword included, action-oriented\r\n**Meta Description:** 140-160 chars, primary keyword, clear value proposition\r\n**Tags:** 5 tags, 2-4 words each, lowercase, topically relevant\r\n**Categories:** 1-2 in format "Parent > Child"\r\n\r\n---\r\n\r\n## VERIFICATION BEFORE OUTPUT\r\n\r\n- [ ] 1000-1200 words ONLY (excluding HTML tags) - STOP if exceeding\r\n- [ ] 6 H2 sections\r\n- [ ] Maximum 2 sections with lists\r\n- [ ] Maximum 2 sections with tables\r\n- [ ] Random sections sequence with differnet format\r\n- [ ] All paragraphs 50-80 words\r\n- [ ] All lists 4-5 items, 15-20 words each\r\n- [ ] All tables 4-5 columns, 5-6 rows, real data\r\n- [ ] No placeholder content anywhere\r\n- [ ] Primary keyword optimized correctly\r\n- [ ] Meta title <60 chars, description <160 chars\r\n- [ ] Valid JSON with escaped quotes\r\n\r\n---\r\n\r\n## IMPORTANT\r\n\r\nReturn ONLY valid JSON. No explanatory text. Ensure word_count reflects actual content words. [] t 1 2025-12-20 20:21:47.267617+00 2025-12-20 12:53:51.906875+00
+4 image_prompt_extraction Extract image prompts from the following article content.\r\n\r\n**ARTICLE TITLE:** {title}\r\n\r\n**ARTICLE CONTENT:** {content}\r\n\r\n**INSTRUCTIONS:**\r\n\r\nExtract image prompts for:\r\n1. **Featured Image:** One main image that represents the article topic\r\n2. **In-Article Images:** Up to {max_images} images that would be useful within the article content\r\n3. **Content Paragraphs (caption):** For each image, write a natural content paragraph (40-60 words) that discusses the topic represented by the image. This should read as regular article content that flows naturally with the surrounding text, NOT as an image description or caption. The paragraph should provide substantive information about the topic while contextually relating to what the image shows.\r\n\r\n**Return a JSON object with this structure:**\r\n\r\n{{\r\n "featured_prompt": "Detailed description of the featured image (max 600 characters)",\r\n "featured_caption": "A 40-60 word paragraph that serves as both an image caption and represents content from the article, providing context naturally",\r\n "in_article_prompts": [\r\n {{\r\n "prompt": "Description of first in-article image (max 600 characters)",\r\n "caption": "A 40-60 word paragraph describing this image while incorporating relevant content from the corresponding section"\r\n }},\r\n {{\r\n "prompt": "Description of second in-article image (max 600 characters)",\r\n "caption": "A 40-60 word paragraph describing this image while incorporating relevant content from the corresponding section"\r\n }}\r\n ]\r\n}}\r\n\r\n**Requirements:**\r\n- Each prompt must be detailed enough for image generation, describing visual elements, style, mood, and composition\r\n- Maximum prompt length: 600 characters per image prompt\r\n- Caption length: 40-60 words each\r\n- Captions should blend image description with article content naturally [] t 1 2025-12-25 02:05:23.641149+00 2025-12-20 12:53:51.908653+00
+\.
+
+
+--
+-- Data for Name: igny8_global_author_profiles; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_global_author_profiles (id, name, description, tone, language, structure_template, category, is_active, created_at, updated_at) FROM stdin;
+\.
+
+
+--
+-- Data for Name: igny8_global_integration_settings; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_global_integration_settings (id, openai_api_key, openai_model, openai_temperature, openai_max_tokens, dalle_api_key, dalle_model, dalle_size, runware_api_key, is_active, last_updated, updated_by_id, image_quality, image_style, runware_model, desktop_image_size, max_in_article_images, default_image_service, bria_api_key, bria_model, anthropic_api_key, anthropic_model, anthropic_temperature, anthropic_max_tokens, default_text_provider) FROM stdin;
+1 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 gpt-4o-mini 0.7 16000 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 dall-e-3 1792x1024 tuHmZhhyUcArJUQ3r0Jiw8ViPaiit0Z3 t 2026-01-03 21:54:01.912554+00 \N hd realistic bria:10@1 1024x1024 4 openai bria-2.3 claude-3-5-sonnet-20241022 0.7 8192 openai
+\.
+
+
+--
+-- Data for Name: igny8_global_module_settings; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_global_module_settings (id, planner_enabled, writer_enabled, thinker_enabled, automation_enabled, site_builder_enabled, linker_enabled, optimizer_enabled, publisher_enabled, updated_at, created_at) FROM stdin;
+1 t t t t f f f f 2025-12-20 23:00:59.378632+00 2025-12-20 21:12:39.039104+00
+\.
+
+
+--
+-- Data for Name: igny8_global_strategies; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_global_strategies (id, name, description, prompt_types, section_logic, category, is_active, created_at, updated_at) FROM stdin;
+\.
+
+
+--
+-- Data for Name: igny8_images; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_images (id, image_type, image_url, image_path, prompt, status, "position", created_at, updated_at, tenant_id, content_id, sector_id, site_id, task_id, delete_reason, deleted_at, deleted_by_id, is_deleted, restore_until, caption) FROM stdin;
+533 in_article https://im.runware.ai/image/ws/2/ii/cec91754-e369-4481-aebe-35c3eb36740d.webp /data/app/igny8/frontend/public/images/ai-images/image_533_1768218752.webp A peaceful home environment showing a person using a back massager while reading a book on a cozy chair. The setting includes soft lighting, a warm blanket, and a peaceful ambiance, emphasizing relaxation. This image illustrates the therapeutic benefits of using a back massager in a calming space. generated 3 2026-01-12 11:47:03.684461+00 2026-01-12 11:47:03.684473+00 90 213 61 21 \N \N \N \N f \N Incorporating a back massager into your relaxation routine can significantly improve your well-being. This image portrays the ideal setting for unwinding, where one can enjoy a good book while experiencing the soothing effects of a massager, highlighting its role in stress relief.
+532 in_article https://im.runware.ai/image/ws/2/ii/d070dce9-a1fd-4f34-b105-407564fecb72.webp /data/app/igny8/frontend/public/images/ai-images/image_532_1768218737.webp A close-up image of a back massager's features, including buttons for different settings, a heating element, and ergonomic design. The background is neutral to keep the focus on the product, which is shown in use on a person's back. This visual emphasizes the technological advancements in back massagers. generated 2 2026-01-12 11:47:03.677128+00 2026-01-12 11:47:03.677146+00 90 213 61 21 \N \N \N \N f \N Modern back massagers come equipped with various features designed to enhance user experience. This image highlights crucial elements like adjustable settings and heating options, showcasing how these advancements cater to individual comfort levels and enhance pain relief.
+531 in_article https://im.runware.ai/image/ws/2/ii/0857c7c6-3db0-47e2-983c-bcd2a4ad3081.webp /data/app/igny8/frontend/public/images/ai-images/image_531_1768218722.webp A side-by-side comparison of different types of back massagers, including handheld devices, massage chairs, and foam rollers. Each product is displayed with clear labels and images showcasing their unique features. The layout is clean and informative, making it easy for readers to visualize their choices. generated 1 2026-01-12 11:47:03.672974+00 2026-01-12 11:47:03.672981+00 90 213 61 21 \N \N \N \N f \N With a variety of back massagers available, choosing the right one can be overwhelming. This comparison showcases several types, from handheld devices to massage chairs, allowing readers to weigh their options based on personal needs and preferences for optimal pain relief.
+529 featured https://im.runware.ai/image/ws/2/ii/b221b67d-4c9b-4b00-a498-e9ab1b82c6c6.webp /data/app/igny8/frontend/public/images/ai-images/image_529_1768218709.webp A serene living room scene featuring a person relaxing on a plush couch with a back massager. The room is softly lit, with warm colors and plants in the background, conveying a sense of comfort and tranquility. The person appears content, with their eyes closed, enjoying the soothing effects of the massager. This image captures the essence of pain relief and relaxation that back massagers provide. generated 0 2026-01-12 11:47:03.662607+00 2026-01-12 11:47:03.662619+00 90 213 61 21 \N \N \N \N f \N Back massagers are essential tools for alleviating discomfort and tension in the back. This image illustrates how these devices can create a peaceful and relaxing environment, allowing individuals to unwind after a long day while effectively managing their back pain.
+530 in_article https://im.runware.ai/image/ws/2/ii/fe77f4b0-998b-473b-ae4e-f0cf927528e2.webp /data/app/igny8/frontend/public/images/ai-images/image_530_1768218695.webp An infographic illustrating the common causes of back pain, featuring icons and illustrations representing various factors like poor posture, heavy lifting, and stress. The design is colorful and engaging, making the information easy to understand. This visual aims to highlight the complexity of back pain and its impact on daily life. generated 0 2026-01-12 11:47:03.669174+00 2026-01-12 11:47:03.669186+00 90 213 61 21 \N \N \N \N f \N Understanding the causes of back pain is crucial for effective management. This infographic highlights various factors, including poor posture and stress, which can contribute to discomfort. Recognizing these causes can help individuals make informed decisions about prevention and treatment, including the use of back massagers.
+537 in_article https://im.runware.ai/image/ws/2/ii/be0052ee-46d4-4ca6-9f37-2f49a7516110.webp /data/app/igny8/frontend/public/images/ai-images/image_537_1768218666.webp A comparison chart showcasing various types of back massagers, including handheld, massage chairs, and vibrating cushions. Each type is represented visually with clear icons and brief descriptions, set against a clean, professional layout that makes it easy to understand the differences and benefits of each type. generated 2 2026-01-12 11:47:16.637938+00 2026-01-12 11:47:16.637944+00 90 214 61 21 \N \N \N \N f \N Understanding the different types of back massagers available can significantly influence your choice. This chart compares handheld units, massage chairs, and vibrating cushions, highlighting their unique features and benefits, helping you make an informed decision based on your specific needs and preferences.
+536 in_article https://im.runware.ai/image/ws/2/ii/8fb8c23a-59a8-4796-9e66-928da74c0e89.webp /data/app/igny8/frontend/public/images/ai-images/image_536_1768218655.webp A vibrant lifestyle image showing a busy individual using a portable back massager at home, surrounded by work-related items such as a laptop and documents. The scene conveys the integration of self-care into a hectic schedule, highlighting the convenience and practicality of modern massagers for people on the go. generated 1 2026-01-12 11:47:16.635642+00 2026-01-12 11:47:16.635647+00 90 214 61 21 \N \N \N \N f \N Your lifestyle plays a pivotal role in selecting a back massager. This image exemplifies how easy it is to incorporate a portable device into a busy day, allowing for quick relief from tension without sacrificing productivity. Choose a model that fits seamlessly into your routine for maximum benefit.
+534 featured https://im.runware.ai/image/ws/2/ii/f01bc427-75e4-449a-b3ac-36da3bd6973b.webp /data/app/igny8/frontend/public/images/ai-images/image_534_1768218641.webp A serene and inviting home setting featuring a person enjoying a relaxing back massage with a modern back massager. The background includes soft lighting, comfortable furniture, and calming decor, suggesting a peaceful environment. The individual shows a look of relief and comfort, highlighting the benefits of using a back massager to alleviate stress and tension. generated 0 2026-01-12 11:47:16.630053+00 2026-01-12 11:47:16.630061+00 90 214 61 21 \N \N \N \N f \N Back massagers can significantly enhance your relaxation routine, providing targeted relief for sore muscles. This image captures the essence of comfort and tranquility, emphasizing how a good back massager can transform your home into a personal spa, allowing you to unwind and rejuvenate effectively.
+547 in_article https://im.runware.ai/image/ws/2/ii/2f375927-288b-45b6-acf1-9b8a55131d26.webp /data/app/igny8/frontend/public/images/ai-images/image_547_1768218525.webp An image of a person using a specific back massager while seated at a desk. The environment is a modern home office, with ergonomic furniture and soft natural lighting. The individual appears relaxed and focused, highlighting the massager's convenience during work hours. generated 2 2026-01-12 11:47:47.475334+00 2026-01-12 11:47:47.47534+00 90 216 61 21 \N \N \N \N f \N Incorporating a back massager into your workspace can significantly enhance comfort and productivity. This image illustrates how a quality massager can provide relief during long hours at a desk, allowing users to maintain focus while enjoying the benefits of relaxation.
+546 in_article https://im.runware.ai/image/ws/2/ii/ba8df9ec-68e1-42ed-a0f1-c67b26f333cd.webp /data/app/igny8/frontend/public/images/ai-images/image_546_1768218512.webp A collage of the top 10 back massagers, featuring each product with a clear image, name, and a brief description. The layout is organized and visually striking, showcasing diverse designs and brands to highlight their unique features. generated 1 2026-01-12 11:47:47.472185+00 2026-01-12 11:47:47.472192+00 90 216 61 21 \N \N \N \N f \N This collage presents the top 10 back massagers of 2023, showcasing a variety of designs and functionalities. Each product is tailored to address different user needs, from portable options for travel to advanced models equipped with heat and multiple massage settings.
+544 featured https://im.runware.ai/image/ws/2/ii/da3ed683-f6c6-4e74-ba24-758a7fd72e6d.webp /data/app/igny8/frontend/public/images/ai-images/image_544_1768218499.webp A serene home setting featuring a cozy living room with a person relaxing in a comfortable chair while using a high-tech back massager. The room is softly lit, with calming colors and plush décor. The massager is sleek and modern, showcasing its ergonomic design. A gentle aura of tranquility envelops the scene, emphasizing the relief and relaxation provided by the massager. generated 0 2026-01-12 11:47:47.466176+00 2026-01-12 11:47:47.466192+00 90 216 61 21 \N \N \N \N f \N The right back massager can transform your relaxation routine, providing effective pain relief and comfort right in your living room. With advancements in technology, these devices now offer ergonomic designs that cater to various needs, making them essential tools for wellness in 2023.
+551 in_article https://im.runware.ai/image/ws/2/ii/be54ae7c-c3ee-478d-9736-c65d8be90225.webp /data/app/igny8/frontend/public/images/ai-images/image_551_1768261302.webp An infographic comparing the benefits of traditional massage and back massagers. The visual features a split design with icons and brief text highlighting key advantages such as relaxation, convenience, and cost. A calm and engaging color palette invites viewers to consider the differences in approach between these two methods. generated 1 2026-01-12 23:40:57.536353+00 2026-01-12 23:40:57.536366+00 90 217 61 21 \N \N \N \N f \N Comparing traditional massage and back massagers reveals distinct benefits for each. While traditional massage offers personalized care and a holistic approach, back massagers provide immediate accessibility and convenience, making them ideal for individuals seeking quick relief without the need for appointments or travel.
+552 in_article https://im.runware.ai/image/ws/2/ii/1f0b50f1-395a-41ba-be26-17f232303a2d.webp /data/app/igny8/frontend/public/images/ai-images/image_552_1768261317.webp A close-up image of a person using a handheld back massager while sitting on a sofa. The person appears relaxed, with a focused expression, demonstrating the ease of use of the device. The background features soft furnishings and warm decor, creating a comfortable atmosphere conducive to relaxation. generated 2 2026-01-12 23:40:57.540096+00 2026-01-12 23:40:57.540108+00 90 217 61 21 \N \N \N \N f \N Using a handheld back massager can be a simple yet effective way to relieve tension after a long day. The ease of application allows individuals to target specific areas of discomfort, ensuring a personalized experience that can fit seamlessly into daily routines, promoting better overall well-being.
+545 in_article https://im.runware.ai/image/ws/2/ii/27279c94-3ee9-4377-80a5-017180788166.webp /data/app/igny8/frontend/public/images/ai-images/image_545_1768218485.webp An infographic displaying the criteria used for selecting the best back massagers, including factors like effectiveness, user reviews, price range, and design features. The design is colorful and engaging, presenting information in a clear and visually appealing manner. generated 0 2026-01-12 11:47:47.469179+00 2026-01-12 11:47:47.469185+00 90 216 61 21 \N \N \N \N f \N Selecting the best back massager involves various criteria, such as effectiveness, user reviews, and design features. This infographic summarizes the key factors that contribute to a massager's performance, helping readers make informed choices when seeking pain relief solutions.
+550 in_article https://im.runware.ai/image/ws/2/ii/2a7abf4d-04ba-443e-aced-2e984b1007cc.webp /data/app/igny8/frontend/public/images/ai-images/image_550_1768261272.webp A detailed image of a back massager in action, showcasing various features such as heat settings, kneading rollers, and ergonomic design. The device is placed on a comfortable chair in a cozy living room, suggesting a home environment. Soft lighting enhances the feeling of relaxation and comfort, illustrating how back massagers can provide therapeutic relief at home. generated 0 2026-01-12 23:40:57.531245+00 2026-01-12 23:40:57.531261+00 90 217 61 21 \N \N \N \N f \N Back massagers are designed for convenience and comfort, allowing users to enjoy therapeutic relief from the comfort of their homes. With features like heat and adjustable settings, these devices can effectively alleviate muscle tension and promote relaxation, making them a popular choice for those with busy lifestyles.
+548 in_article https://im.runware.ai/image/ws/2/ii/a584d1e8-5971-4047-86aa-c40bb5aa05d4.webp /data/app/igny8/frontend/public/images/ai-images/image_548_1768218538.webp A split image showing the pros and cons of a popular back massager, with one side listing advantages like portability and user-friendly design, and the other side detailing drawbacks like price or limited features. The design is balanced and informative. generated 3 2026-01-12 11:47:47.478183+00 2026-01-12 11:47:47.47819+00 90 216 61 21 \N \N \N \N f \N Understanding the pros and cons of each back massager is essential for making an informed purchase. This image effectively breaks down the advantages and limitations of a popular model, ensuring readers are equipped with comprehensive insights before deciding on their ideal product.
+549 featured https://im.runware.ai/image/ws/2/ii/50986f53-f385-4b27-800e-e6b492c02932.webp /data/app/igny8/frontend/public/images/ai-images/image_549_1768261287.webp An inviting and serene spa environment showcasing a professional masseuse performing a traditional massage on a client. The room is softly lit with candles, and soothing colors create a calming atmosphere. Essential oils and massage tools are visible, highlighting the luxurious experience of traditional massage therapy. This image embodies the relaxation and therapeutic benefits associated with professional massages. generated 0 2026-01-12 23:40:57.523096+00 2026-01-12 23:40:57.523114+00 90 217 61 21 \N \N \N \N f \N Traditional massage offers a unique, hands-on approach to relaxation and pain relief, often performed in a serene spa setting. The use of skilled techniques by a professional can enhance physical well-being and promote mental tranquility, making it a cherished method for many seeking relief from stress and muscle tension.
+471 featured https://im.runware.ai/image/ws/2/ii/9c007f87-9103-4946-abc5-0e9b54bbf3fb.webp /data/app/igny8/frontend/public/images/ai-images/image_471_1766976180.webp A calm home setting with a person sitting on a sofa using a curved back massager hook on their upper back and shoulder. Several different manual back massager sticks and hooks lie on a coffee table nearby. Bright natural light, clean modern decor, soft neutral colors, realistic photography style, health and wellness mood. generated 0 2025-12-29 02:42:22.270608+00 2025-12-29 02:42:22.270618+00 90 203 61 21 \N \N \N \N f \N Manual back massager sticks and hooks make it easy to target stubborn shoulder and upper back knots without batteries, noise, or complicated settings. By focusing pressure into small trigger points and letting you control the exact angle and intensity, these simple tools can deliver clinic-style relief right in your living room at a fraction of the cost.
+478 in_article https://im.runware.ai/image/ws/2/ii/23ee1bc8-73d8-4721-8e58-2e03cf072246.webp /data/app/igny8/frontend/public/images/ai-images/image_478_1767227013.webp Collage of four types of back massagers with heat: a large shiatsu chair pad, a compact heated lumbar cushion, a wand-style handheld massager, and a massage gun with a heating attachment. Clean, bright background, each product clearly separated with subtle icons showing features like heat, vibration, and intensity levels. failed 1 2026-01-01 00:10:32.313915+00 2026-01-01 00:10:32.31392+00 90 204 61 21 \N \N \N \N f \N Today’s market includes everything from full-back shiatsu chair pads to small heated cushions, handheld wands, and powerful massage guns. Your choice should reflect how and when you experience pain. Office workers may prefer a heated lumbar cushion, while active users might lean toward a massage gun with optional heating features.
+520 in_article https://oaidalleapiprodscus.blob.core.windows.net/private/org-d6y6V9UCfxJGPrrw8U9X7iX2/user-Hcrg9zi6BPbsvyJfu4qZ5wyy/img-ztrIVBN5KaDMBmZif7oE3PiO.png?st=2026-01-03T20%3A56%3A45Z&se=2026-01-03T22%3A56%3A45Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=52f8f7b3-ca8d-4b21-9807-8b9df114d84c&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2026-01-03T13%3A06%3A51Z&ske=2026-01-04T13%3A06%3A51Z&sks=b&skv=2024-08-04&sig=0kWqvELgJgiulO5nhf3HpAS4HnIGUP8UWCkaLHY2QAQ%3D /data/app/igny8/frontend/public/images/ai-images/image_520_1767477405.png Close-up of a traditional handheld back massager with a long curved handle, multiple interchangeable heads, and visible heating element indicator light resting on a couch arm. A person’s hand reaches for it in a cozy living room at night, warm ambient lighting, lifestyle product photography, clean and realistic. generated 0 2026-01-03 18:10:26.070011+00 2026-01-03 18:10:26.07002+00 90 206 61 21 \N \N \N \N f \N Handheld back massagers typically feature a longer handle, lighter weight, and sometimes built-in heat, making them easy to use while relaxing on the couch. Their design favors reach and comfort over raw power, which suits people dealing with everyday stiffness who want something simple to operate themselves at home.
+513 in_article https://im.runware.ai/image/ws/2/ii/b1d9c5aa-0e79-4eba-a984-4a084f484724.webp /data/app/igny8/frontend/public/images/ai-images/image_513_1767226385.webp Cozy interior of a neighborhood pharmacy or drugstore aisle, with a small but tidy section of back massagers, heating wraps, and pain relief products. A person in business-casual clothing studies a compact handheld massager next to over-the-counter pain medications. Soft lighting, approachable atmosphere, realistic documentary style. generated 3 2026-01-01 00:11:29.138431+00 2026-01-01 00:11:29.138446+00 90 211 61 21 \N \N \N \N f \N Drugstores and pharmacies typically offer a smaller selection of back massagers, but they’re convenient when you need quick relief. You’ll usually find compact handheld units, heated wraps, and lower-priced cushions near the pain relief aisle. While choices are limited, extended hours and multiple neighborhood locations make pharmacies a practical last-minute option.
+467 in_article https://im.runware.ai/image/ws/2/ii/aa0c0ebe-c40c-4523-97ac-efaddb9f8619.webp /data/app/igny8/frontend/public/images/ai-images/image_467_1766976250.webp Close-up of a back massager attached to an office chair, showing multiple massage nodes aligned with the spine, control panel on the side, and adjustable straps around the chair back. Neutral background, clear focus on the device’s structure and how it fits on the chair, realistic product photography. generated 0 2025-12-29 02:42:14.283031+00 2025-12-29 02:42:14.283045+00 90 202 61 21 \N \N \N \N f \N A back massager for chair is designed to strap securely onto most office or dining chairs, aligning its massage nodes with your spine. By delivering continuous, targeted pressure while you sit, it replaces the need to stop and use a handheld device, making relief part of your normal work routine.
+535 in_article https://im.runware.ai/image/ws/2/ii/ac941219-4591-402f-9e86-130583b7b61b.webp /data/app/igny8/frontend/public/images/ai-images/image_535_1768218626.webp An infographic displaying common pain points associated with back discomfort, featuring an outline of the human body with highlighted areas such as the lower back, shoulders, and neck. The design includes labeled pain points and statistics about back pain prevalence, set against a soothing color palette that evokes calmness and healing. generated 0 2026-01-12 11:47:16.632788+00 2026-01-12 11:47:16.632796+00 90 214 61 21 \N \N \N \N f \N Identifying your pain points is crucial when choosing a back massager. This infographic illustrates common areas of discomfort, helping you understand where you need the most relief. By pinpointing these areas, you can select a massager that targets your specific issues, ensuring more effective and personalized treatment.
+538 in_article https://im.runware.ai/image/ws/2/ii/2dd75ee8-24b5-40ce-86e1-b407ae89bab0.webp /data/app/igny8/frontend/public/images/ai-images/image_538_1768218680.webp An image of a person reviewing a selection of back massagers in a retail store, examining various models while holding a price tag. The shelves are lined with a diverse range of options, showcasing different brands and styles, with a focus on the importance of budget considerations in the buying process. generated 3 2026-01-12 11:47:16.640731+00 2026-01-12 11:47:16.640771+00 90 214 61 21 \N \N \N \N f \N Budget considerations are essential when selecting a back massager. This image illustrates the variety available in store, encouraging potential buyers to evaluate their options. It's important to balance quality with affordability, ensuring you invest in a product that meets your needs without overspending.
+490 in_article https://im.runware.ai/image/ws/2/ii/03cf71ab-beab-497b-bab6-b96effd94d0f.webp /data/app/igny8/frontend/public/images/ai-images/image_490_1767474829.webp Flat lay of several different back massagers on a wooden table: two heated handheld back massagers with long handles, two compact massage guns, a digital thermometer showing 115°F, and a notepad listing criteria like “104–131°F heat,” “25+ lb stall force,” and “4.3★, 1000+ reviews.” Bright, neutral lighting, realistic, editorial style. generated 0 2026-01-01 00:10:56.568014+00 2026-01-01 00:10:56.568022+00 90 207 61 21 \N \N \N \N f \N To narrow more than 30 contenders, we focused on measurable criteria instead of hype. That meant verifying heat output stayed between 104°F and 131°F, checking stall force above 25 pounds for meaningful pressure, and filtering for real-world reliability by only considering models with at least 1,000 reviews and an average rating of 4.3 stars or higher.
+491 in_article https://im.runware.ai/image/ws/2/ii/d4e676f8-c689-452f-8e0f-70e90ea81554.webp /data/app/igny8/frontend/public/images/ai-images/image_491_1767474849.webp Close-up of a premium heated back massager draped over a person’s shoulders and upper back, visible red heat zones, simple control panel with heat and intensity buttons. The user sits comfortably at a desk, relaxed expression, soft daylight from a window. Clean, realistic product-focused shot with subtle emphasis on comfort and build quality. generated 1 2026-01-01 00:10:56.578832+00 2026-01-01 00:10:56.57884+00 90 207 61 21 \N \N \N \N f \N Our top overall heated back massager balances comfort, power, and ease of use. The contoured design sits securely across your shoulders while the heat function warms gradually to a steady, therapeutic temperature. Simple controls let you adjust intensity without twisting or straining, making it practical for daily use while working, reading, or watching TV.
+493 in_article https://im.runware.ai/image/ws/2/ii/31303082-4938-48d6-9c72-073f42ef0f9a.webp /data/app/igny8/frontend/public/images/ai-images/image_493_1767474889.webp Athletic person sitting on a gym bench using a compact massage gun on their lower back, visible interchangeable head attachments on the bench, subtle LED indicators on the device. Gym background with weights slightly out of focus. Realistic, high-contrast lighting, energetic yet controlled mood emphasizing deep tissue relief and recovery. generated 3 2026-01-01 00:10:56.599519+00 2026-01-01 00:10:56.599529+00 90 207 61 21 \N \N \N \N f \N When you need deep tissue relief, a massage gun designed for the back offers more targeted power than classic handheld models. Higher stall force lets you press into tight muscles without stalling the motor, while multiple head attachments adapt to different areas so you can address stubborn knots after lifting sessions or long days of physical work.
+460 in_article https://im.runware.ai/image/ws/2/ii/a3da5fe5-38a5-460d-beef-a809c587632d.webp /data/app/igny8/frontend/public/images/ai-images/image_460_1766976509.webp Close-up of a person using a U-shaped back and neck massager around the shoulders, with heat function glowing softly, and another hand holding a manual massage hook reaching between the shoulder blades. Cozy home setting, soft lighting, focus on upper back and neck area, expression of relief and concentration. generated 3 2025-12-29 02:41:57.538195+00 2025-12-29 02:41:57.538205+00 90 200 61 21 \N \N \N \N f \N For upper back and neck pain, targeted tools can be more effective than broad, full-back devices. A U-shaped massager wraps around tense shoulders, combining kneading with soothing heat, while a manual hook lets you lean into specific knots between the shoulder blades, adjusting pressure precisely where your muscles are most reactive.
+458 in_article https://im.runware.ai/image/ws/2/ii/cde9bd6a-39b0-4928-810c-4e9f937b352d.webp /data/app/igny8/frontend/public/images/ai-images/image_458_1766976473.webp Flat-lay of different back massagers on a neutral background: a full-featured massage chair, a portable back massager cushion, a U-shaped neck and shoulder massager, a handheld percussion gun, and a manual trigger-point hook. Clean, well-lit, organized composition with labels or subtle icons suggesting their different uses. generated 1 2025-12-29 02:41:57.527156+00 2025-12-29 02:41:57.527161+00 90 200 61 21 \N \N \N \N f \N Back massagers range from full-body chairs that mimic a therapist’s hands to simple manual tools that pinpoint a single knot. Electric cushions and U-shaped neck devices are ideal for everyday use, while percussion guns and massage hooks excel at breaking up stubborn trigger points when you need focused, deeper work.
+456 featured https://im.runware.ai/image/ws/2/ii/5d478ee0-fd88-489d-af75-8f502aaef1ec.webp /data/app/igny8/frontend/public/images/ai-images/image_456_1766976453.webp Calm, modern living room with a young adult sitting in an ergonomic chair using a sleek back and neck massager. Soft evening light, neutral colors, subtle tech details on the device, relaxed facial expression. Emphasis on comfort, relief, and self-care at home. Photorealistic, clean, wellness-focused aesthetic. generated 0 2025-12-29 02:41:57.5095+00 2025-12-29 02:41:57.509509+00 90 200 61 21 \N \N \N \N f \N Consistent, targeted massage can turn a typical evening of stiffness into a short, restorative ritual. A well-chosen back massager acts like an on-demand therapist, easing tight bands along the spine and neck in just 10–20 minutes, helping counteract the micro-strains that build up from desks, driving, and constant screen time.
+457 in_article https://im.runware.ai/image/ws/2/ii/69bd072e-1147-4606-a966-199329371fac.webp /data/app/igny8/frontend/public/images/ai-images/image_457_1766976435.webp Medical-style illustration of upper back, neck, and shoulders showing major muscle groups and common tension areas highlighted in red. Subtle overlay of a person sitting at a desk, hunched over a laptop. Clean, educational, slightly stylized but realistic anatomy, white background, labels minimized for clarity, health and wellness theme. generated 0 2025-12-29 02:41:57.521742+00 2025-12-29 02:41:57.52175+00 90 200 61 21 \N \N \N \N f \N Most back, neck, and shoulder pain starts with subtle postural habits that overload the same muscles day after day. When you hunch over a screen or drive for hours, the upper trapezius, rhomboids, and neck extensors tighten and shorten, restricting circulation and creating those familiar burning bands between your shoulder blades and up into your neck.
+463 in_article https://im.runware.ai/image/ws/2/ii/a8ec38b7-06b2-4ad9-bf92-ff3043e99e20.webp /data/app/igny8/frontend/public/images/ai-images/image_463_1766976378.webp Scene of a person reading a safety checklist on a tablet while holding a back hook massager. Callout icons highlight sensitive areas like the spine and neck joints, with soft red zones to avoid. Calm indoor environment, neutral tones, clear educational feel, realistic style emphasizing safety and careful use. generated 1 2025-12-29 02:42:06.136586+00 2025-12-29 02:42:06.136597+00 90 201 61 21 \N \N \N \N f \N Before you start, it’s important to review a few safety basics. Avoid pressing directly on the spine or front of the neck, and never force through sharp or radiating pain. Instead, aim for moderate, tolerable pressure on muscle tissue only, gradually increasing intensity as your body warms and relaxes.
+528 in_article https://im.runware.ai/image/ws/2/ii/2ed11307-09af-4614-bb33-460567e7d0f3.webp /data/app/igny8/frontend/public/images/ai-images/image_528_1767466124.webp Split-screen style image: left side shows a shopper testing a demo back massager cushion in a Walmart store; right side shows a person at home browsing an expanded selection of back massagers on Walmart’s website. Consistent realistic style, clear contrast between in-store immediacy and online variety. failed 3 2026-01-03 18:14:28.841815+00 2026-01-03 18:14:28.841824+00 90 210 61 21 \N \N \N \N f \N Comparing in-store and online-only back massager options at Walmart often comes down to priorities. In-store models offer instant pickup and the chance to inspect size and controls, while online listings usually include more advanced features and brands. Balancing speed, price, and functionality helps you decide which buying path makes the most sense.
+465 in_article https://im.runware.ai/image/ws/2/ii/11c34e79-f1a5-43a2-9698-fc02d739370e.webp /data/app/igny8/frontend/public/images/ai-images/image_465_1766976416.webp Close-up of a person using a back hook massager on the side of the neck and upper shoulder (trapezius). Step-by-step overlay numbers show hand placement and direction of pull, with the person’s face relaxed. Warm lighting, realistic style, clinical yet approachable instructional mood. generated 3 2025-12-29 02:42:06.152214+00 2025-12-29 02:42:06.152221+00 90 201 61 21 \N \N \N \N f \N When working on neck tension, start at the upper shoulder where the trapezius muscle meets the neck. Hook the knob over a tender spot, then gently pull down or slightly forward while breathing slowly. Adjust the angle millimeter by millimeter so you can explore around joints and sensitive structures with precision.
+464 in_article https://im.runware.ai/image/ws/2/ii/09b9302e-f29c-4483-959e-609e233c1fe3.webp /data/app/igny8/frontend/public/images/ai-images/image_464_1766976397.webp Side view of a person sitting upright in a chair, feet flat on the floor, using a back hook massager on the upper back between the shoulder blade and spine. Arrows illustrate correct body alignment and pulling direction. Bright, realistic, instructional composition focused on posture and positioning. generated 2 2025-12-29 02:42:06.144392+00 2025-12-29 02:42:06.144399+00 90 201 61 21 \N \N \N \N f \N Body positioning matters as much as the tool itself. Sit or stand with a neutral spine, feet grounded, and shoulders relaxed so you can pull the hook smoothly instead of tensing up. Good alignment lets you deliver steady pressure to knots around the shoulder blades without overworking your hands or wrists.
+470 in_article https://im.runware.ai/image/ws/2/ii/a2728b99-edcc-4a3c-a9a3-3aeddf5d226d.webp /data/app/igny8/frontend/public/images/ai-images/image_470_1766976324.webp Interior of a car with a driver’s seat fitted with a compact back massager pad and another on a living room recliner in the background, split-scene composition. The car shows the device powered and glowing slightly; the home scene is cozy with warm lighting. Realistic style, lifestyle-focused. generated 3 2025-12-29 02:42:14.305096+00 2025-12-29 02:42:14.305101+00 90 202 61 21 \N \N \N \N f \N These massagers aren’t just for the office. A compact model can strap onto your car seat to ease tightness during commutes, while a larger version can live on your favorite recliner at home. Consistent use in both settings helps counteract the cumulative strain of hours spent sitting each day.
+469 in_article https://im.runware.ai/image/ws/2/ii/e84d6d67-5799-4a28-bcf8-597ff320be17.webp /data/app/igny8/frontend/public/images/ai-images/image_469_1766976304.webp Open-plan office with several employees at desks; one person sits in a task chair fitted with a back massager, discreetly visible. The person looks focused on their computer, relaxed posture, soft daylight from windows. Professional, realistic photography emphasizing productivity and comfort together. generated 2 2025-12-29 02:42:14.299226+00 2025-12-29 02:42:14.299235+00 90 202 61 21 \N \N \N \N f \N At work, a chair back massager can quietly tackle the stiffness that builds during long stretches at your desk. Short 10–20 minute sessions between meetings can ease muscle tension by around 15–30% over weeks, which many people notice as fewer tension headaches, better posture, and less late-day fatigue.
+466 featured https://im.runware.ai/image/ws/2/ii/b147aee0-6291-496e-80a9-0a17cf1d296f.webp /data/app/igny8/frontend/public/images/ai-images/image_466_1766976269.webp A modern home office scene with a person sitting in an ergonomic chair fitted with a sleek black back massager pad. The massager shows glowing red heated nodes along the spine area. Soft natural light, warm and relaxing mood, clean minimal desk setup with laptop, realistic photography style, high detail. generated 0 2025-12-29 02:42:14.272587+00 2025-12-29 02:42:14.272597+00 90 202 61 21 \N \N \N \N f \N Turning an ordinary chair into a relaxation zone is as simple as strapping on a back massager. While you type or take calls, rolling or kneading nodes work along your spine for 10–20 minutes, easing stiffness that builds from long hours of sitting and helping reduce evening fatigue over time.
+473 in_article https://im.runware.ai/image/ws/2/ii/b18a69c6-afe2-46ef-986c-ada904a5052f.webp /data/app/igny8/frontend/public/images/ai-images/image_473_1766976197.webp Flat lay of several back massager tools on a wooden surface: a straight massage stick with knobs, an S-shaped hook, a compact double-ball roller. Small labels indicate features like grip texture, knob size, and reach. Clean, bright overhead lighting, minimalist product photography style. generated 1 2025-12-29 02:42:22.284404+00 2025-12-29 02:42:22.284409+00 90 203 61 21 \N \N \N \N f \N Choosing the right back and shoulder massager tool comes down to reach, handle comfort, and how focused the pressure feels. Longer hooks are ideal for upper back and neck trigger points, while straight sticks with textured grips work well along the spine. Knob size also matters—smaller tips dig deeper, larger ones feel more diffuse.
+459 in_article https://im.runware.ai/image/ws/2/ii/4b319322-0da5-4669-a1eb-075966677a4b.webp /data/app/igny8/frontend/public/images/ai-images/image_459_1766976490.webp Office setting with a person sitting in an ergonomic desk chair fitted with a sleek back massager cushion. The device shows a gentle glow where rollers are working along the spine. Computer and tidy desk in view, natural daylight, relaxed posture, subtle sense of productivity and comfort. Photorealistic, professional yet cozy. generated 2 2025-12-29 02:41:57.53232+00 2025-12-29 02:41:57.532326+00 90 200 61 21 \N \N \N \N f \N A back massager for chair is one of the easiest ways to build relief into your day without changing your routine. While you answer emails or join virtual meetings, rollers or vibration panels quietly work along your spine, loosening tight spots and improving circulation so your back doesn’t feel wrecked by the time you log off.
+461 featured https://im.runware.ai/image/ws/2/ii/733df960-90e6-4bc2-9a57-520fb72ebbc5.webp /data/app/igny8/frontend/public/images/ai-images/image_461_1766976360.webp Bright, clean home setting with a person sitting on a couch using a blue plastic back hook massager on their upper back and shoulder. Focus on the curved hook applying pressure to a knot, relaxed posture, natural daylight, soft neutral colors, realistic photography style, wellness and self-care mood. generated 0 2025-12-29 02:42:06.118719+00 2025-12-29 02:42:06.118728+00 90 201 61 21 \N \N \N \N f \N A back hook massager makes it easy to reach stubborn knots along your neck, shoulders, and upper back without straining your hands. By using the curved handle as a lever, you can generate firm, controlled pressure while staying relaxed, turning a few minutes of focused self-massage into an effective daily self-care routine.
+462 in_article https://im.runware.ai/image/ws/2/ii/84f7c35b-c87a-4632-8d9e-a9401233f985.webp /data/app/igny8/frontend/public/images/ai-images/image_462_1766976342.webp Close-up on a back hook massager against a white background, showing its curved hook, multiple knobs, and ergonomic handle. Next to it, a simple diagram overlay illustrates how the lever action multiplies force on a muscle knot. Clean, instructional, realistic style suitable for an educational health article. generated 0 2025-12-29 02:42:06.1276+00 2025-12-29 02:42:06.127611+00 90 201 61 21 \N \N \N \N f \N A back hook massager is designed with curves and knobs that match the contours of your back, shoulders, and neck. By pulling gently on the handle, you use simple leverage to direct 10–20 pounds of pressure into a single tight spot, making it far more precise than most electric massage devices.
+468 in_article https://im.runware.ai/image/ws/2/ii/7a30ec78-d6e7-45d4-94ef-19fecfaeb966.webp /data/app/igny8/frontend/public/images/ai-images/image_468_1766976286.webp Diagram-style scene showing key features of a chair back massager: labeled shiatsu nodes, rolling track, vibration zones, and a glowing heat function around 40–50°C. Clean, modern infographic look with subtle gradients, neutral background, and simple icons to highlight adjustability, presets, and safety features. generated 1 2025-12-29 02:42:14.291307+00 2025-12-29 02:42:14.291314+00 90 202 61 21 \N \N \N \N f \N When comparing models, it helps to understand the core features: mechanical massage heads that mimic shiatsu or rolling, vibration motors for broader relaxation, and optional heat in the 40–50°C range to boost circulation. Preset programs and adjustable intensities let you tailor each session to your comfort level.
+475 in_article https://im.runware.ai/image/ws/2/ii/a82aa24b-5d4b-48ec-ba96-692eb7b2af0e.webp /data/app/igny8/frontend/public/images/ai-images/image_475_1766976234.webp A close, three-quarter view of a person using an S-shaped back hook massager over their shoulder to reach the upper back and neck. The hook has multiple rounded knobs along its curve. Soft indoor lighting, neutral background, realistic wellness photography emphasizing deep, focused pressure on shoulder and neck knots. generated 3 2025-12-29 02:42:22.295585+00 2025-12-29 02:42:22.29559+00 90 203 61 21 \N \N \N \N f \N Back hook massagers excel at reaching deep knots at the tops of the shoulders and along the base of the neck. By pulling the hook’s handle forward, you can generate strong, controlled pressure on hard-to-reach spots while keeping your arms relaxed, making it easier to work on stubborn tension without tiring yourself out.
+474 in_article https://im.runware.ai/image/ws/2/ii/0381116b-723e-48fc-bcf4-fa3652b96443.webp /data/app/igny8/frontend/public/images/ai-images/image_474_1766976215.webp A person in casual clothes standing and using a straight back massager stick across their upper back, hands gripping both ends to apply pressure. Cozy living room setting, natural daylight, realistic photography. The stick has ergonomic handles and a slightly curved center with small massage nodes. generated 2 2025-12-29 02:42:22.290268+00 2025-12-29 02:42:22.290276+00 90 203 61 21 \N \N \N \N f \N For everyday use, a well-designed back massager stick should feel intuitive to hold and easy to maneuver across the upper back. Ergonomic handles reduce strain in the wrists and forearms, while a slightly curved center with small nodes lets you roll or press into tight bands of muscle without needing extreme strength or flexibility.
+472 in_article https://im.runware.ai/image/ws/2/ii/160b2c31-fb5f-48ad-a6af-8f67e4a78562.webp /data/app/igny8/frontend/public/images/ai-images/image_472_1766976162.webp Close-up of a person using a curved back massager hook on a trigger point between the shoulder blade and spine. Transparent anatomical overlay subtly shows muscle knots and tension lines. Neutral background, soft lighting, realistic medical-education style, emphasizing how precise pressure is applied to specific points in the upper back. generated 0 2025-12-29 02:42:22.278181+00 2025-12-29 02:42:22.278186+00 90 203 61 21 \N \N \N \N f \N When you press a back massager hook into a tight spot between the shoulder blade and spine, you’re targeting a trigger point rather than spreading force across a broad area. Staying just below a 7 out of 10 on the pain scale helps the muscle release instead of tensing up, gradually easing that deep, concrete-like tightness.
+483 in_article https://im.runware.ai/image/ws/2/ii/9c1b010f-60d6-47d3-b676-9f11c3375dbc.webp /data/app/igny8/frontend/public/images/ai-images/image_483_1767226958.webp A person sitting on a sofa with a heated back massager, next to a visible timer or phone showing 15–20 minutes, and a weekly planner with several short sessions marked. Warm, relaxed living room, realistic style, emphasizing moderation and routine rather than intensity, health and self-care theme. generated 3 2026-01-01 00:10:39.723812+00 2026-01-01 00:10:39.723817+00 90 205 61 21 \N \N \N \N f \N Short, consistent sessions are safer and more effective than long, intense ones. Aim for about 10–20 minutes per area, then give your tissues time to recover. Building a routine of moderate daily use lets your muscles adapt to the combination of heat and pressure without causing next-day soreness or fatigue.
+482 in_article https://im.runware.ai/image/ws/2/ii/ce08e24e-c173-472c-bf59-26032de65377.webp /data/app/igny8/frontend/public/images/ai-images/image_482_1767226942.webp Three-panel illustration showing proper positioning of a heated back massager: seated against a chair for upper back, reclining with support for mid-back, and lying with knees bent for lower back. Neutral, clear colors, realistic but simplified style focused on posture and spinal alignment, no text, educational look. generated 2 2026-01-01 00:10:39.718575+00 2026-01-01 00:10:39.718581+00 90 205 61 21 \N \N \N \N f \N Positioning changes how the massage feels on different parts of your back. For upper and mid-back, leaning against a supportive chair or cushion helps the device contact muscles without digging into bone. For the lower back, lying down with knees bent keeps your spine neutral so the heat and pressure spread more comfortably.
+481 in_article https://im.runware.ai/image/ws/2/ii/f610712c-e634-4848-9836-55cd31ff4960.webp /data/app/igny8/frontend/public/images/ai-images/image_481_1767226923.webp Bathroom or bedroom scene where a person gently stretches their back and applies a thin towel over the lower back area before using a heated back massager. Soft, natural lighting, tidy space with a water bottle and small towel nearby, realistic wellness photography style, calm and prepared atmosphere. generated 1 2026-01-01 00:10:39.712838+00 2026-01-01 00:10:39.712844+00 90 205 61 21 \N \N \N \N f \N Preparing your body and environment makes each massage session more effective. Light stretching, staying hydrated, and placing a thin layer of fabric between the device and your skin help your back tolerate both pressure and heat. This simple setup reduces the risk of hot spots or friction while making the warmth feel more evenly distributed.
+479 featured https://im.runware.ai/image/ws/2/ii/a3bc8606-8155-4ea7-8dd6-82a6c2c88ba8.webp /data/app/igny8/frontend/public/images/ai-images/image_479_1767226907.webp A calm, modern bedroom scene with a person lying on their back on a bed, using an electric back massager with visible soft red heat glow along the spine. Warm, neutral lighting, cozy blankets, and a relaxed expression emphasize comfort and safety. Clean, realistic style, health and wellness magazine photography, no branding, diverse adult. generated 0 2026-01-01 00:10:39.700655+00 2026-01-01 00:10:39.700665+00 90 205 61 21 \N \N \N \N f \N Using a back massager with heat should feel soothing, not intense or overwhelming. Combining gentle mechanical pressure with moderate warmth can boost blood flow and ease tight muscles when matched to your body’s needs. The goal is to create a calm, comfortable environment where you can relax while the device does gradual, controlled work.
+480 in_article https://im.runware.ai/image/ws/2/ii/e7a787a3-0abe-49ae-b5fd-1eed55550b4d.webp /data/app/igny8/frontend/public/images/ai-images/image_480_1767226889.webp Close-up of a handheld back massager with heat, showing clearly labeled intensity, speed, and heat buttons or dials. A person’s hands adjust the settings while an illustrated overlay highlights different levels. Clean, bright background, instructional yet friendly feel, realistic style suitable for a health guide, no visible branding. generated 0 2026-01-01 00:10:39.706657+00 2026-01-01 00:10:39.706663+00 90 205 61 21 \N \N \N \N f \N Before you start, take a moment to understand your back massager’s settings. Identify which controls adjust intensity, speed, and heat, and begin at the lowest levels. Gradually increase only if your muscles feel comfortable. This step helps you avoid excessive pressure or temperature that can irritate sensitive tissues instead of relaxing them.
+489 featured https://im.runware.ai/image/ws/2/ii/4af61c2f-126d-45d0-b1ed-2bfb0727eb48.webp /data/app/igny8/frontend/public/images/ai-images/image_489_1767474804.webp A modern, cozy living room scene with a person sitting on a sofa using a sleek heated back massager on their upper back, soft red/orange glow indicating soothing heat. Nearby on the coffee table, a compact massage gun rests beside a laptop and water bottle. Warm, natural lighting, realistic photography style, clean, health-focused aesthetic. generated 0 2026-01-01 00:10:56.560641+00 2026-01-01 00:10:56.560651+00 90 207 61 21 \N \N \N \N f \N Choosing the best back massager with heat starts with understanding how you actually use it day to day. Whether you’re unwinding on the couch after work or recovering from a workout, a good model should combine consistent heat, ergonomic reach, and enough power to loosen knots without leaving your back feeling sore or bruised.
+497 in_article https://im.runware.ai/image/ws/2/ii/547dfcda-939d-4630-a868-bdf5563fd0ab.webp /data/app/igny8/frontend/public/images/ai-images/image_497_1767226652.webp Smartphone in a person’s hand showing a map app with multiple pins labeled pharmacy, big-box store, and wellness shop. On-screen search bar reads “back massager near me.” Background subtly shows a living room setting. Bright, clear interface, realistic style, emphasizing local search and planning purchases. generated 2 2026-01-01 00:11:05.915312+00 2026-01-01 00:11:05.915319+00 90 208 61 21 \N \N \N \N f \N Using a “back massager near me” search strategically means going beyond the first result. Check which locations actually list massagers in stock, filter by price range, and read quick reviews. This lets you plan two or three promising stops instead of driving blindly from store to store hoping they carry what you need.
+496 in_article https://im.runware.ai/image/ws/2/ii/cde5a305-7248-40b2-af4a-9331d2099f45.webp /data/app/igny8/frontend/public/images/ai-images/image_496_1767226632.webp Close-up of several types of back massagers displayed on a store shelf: a long-handled wand, a compact vibrating pad, a large shiatsu back cushion with rollers, and a high-end massage gun. Clear packaging highlights features like heat, intensity, and targeted zones. Clean, well-lit, realistic product-focused composition. generated 1 2026-01-01 00:11:05.907601+00 2026-01-01 00:11:05.907611+00 90 208 61 21 \N \N \N \N f \N Local stores usually carry a mix of back massager styles, from simple vibrating wands to more advanced shiatsu cushions with rotating nodes and heat. Seeing them side by side makes it easier to understand how each design targets different areas of your back and what level of intensity fits your comfort level.
+498 in_article https://im.runware.ai/image/ws/2/ii/8c98e1e6-cd15-48f6-95df-220b9224544c.webp /data/app/igny8/frontend/public/images/ai-images/image_498_1767226670.webp Interior of a Walmart store health and wellness aisle with a dedicated section for massagers. Shelves display affordable handheld massagers, mid-range shiatsu cushions, and a few premium models. Overhead signage and price tags are visible. Bright fluorescent lighting, wide-angle view, realistic style, everyday shopping environment. generated 3 2026-01-01 00:11:05.924805+00 2026-01-01 00:11:05.924813+00 90 208 61 21 \N \N \N \N f \N At Walmart, back massagers are often grouped with other personal care or wellness devices, making it easy to compare prices quickly. You’ll usually find budget-friendly handheld models alongside mid-range shiatsu cushions. Checking in-store labels for features and return policies helps you decide whether to grab a basic option or invest in something more advanced.
+502 in_article https://im.runware.ai/image/ws/2/ii/d3f240f4-c8a6-419f-b88e-30017aaee82c.webp /data/app/igny8/frontend/public/images/ai-images/image_502_1767226559.webp A split-screen style image showing a smartphone displaying live inventory for back massagers at major retailers like a big-box store and a pharmacy. Product thumbnails show different massager styles with stock indicators such as 'In Stock' and 'Low Stock'. Clean, bright interface, realistic photography, neutral background. generated 2 2026-01-01 00:11:13.452513+00 2026-01-01 00:11:13.452518+00 90 209 61 21 \N \N \N \N f \N Checking live inventory at major retailers can save you wasted trips and frustration. Many store apps and websites now show whether specific back massagers are in stock at locations near you. Use this feature to confirm availability, compare models, and even place items on hold before you head out the door.
+501 in_article https://im.runware.ai/image/ws/2/ii/3cefaae5-0ded-4edb-b50b-50c64471ec33.webp /data/app/igny8/frontend/public/images/ai-images/image_501_1767226540.webp Over-the-shoulder view of a person at a desk using a laptop and smartphone, both screens showing search results for 'back massager near me' with map pins, filters for price, ratings, and distance. Clean, modern workspace, daylight, realistic photography, slight depth of field focusing on the screens. generated 1 2026-01-01 00:11:13.447508+00 2026-01-01 00:11:13.447513+00 90 209 61 21 \N \N \N \N f \N Typing “back massager near me” is just the starting point. Use filters for distance, price range, and customer ratings to refine results, then compare a few promising options side by side. This smart approach helps you avoid endless scrolling and quickly spot stores that carry the kinds of massagers you actually want.
+499 featured https://im.runware.ai/image/ws/2/ii/963fb586-73e4-46e7-9669-785f4d128c2e.webp /data/app/igny8/frontend/public/images/ai-images/image_499_1767226522.webp A cozy living room scene with a person sitting comfortably in an armchair using an electric back massager on their upper back, smartphone in hand showing a map with nearby store pins. Warm natural light, modern but relatable home decor, calm and relieved facial expression, realistic photography style, soft focus background emphasizing comfort and convenience. generated 0 2026-01-01 00:11:13.433743+00 2026-01-01 00:11:13.433751+00 90 209 61 21 \N \N \N \N f \N Finding the best back massager near you doesn’t have to be stressful or time-consuming. By combining online search tools with a clear idea of what you need, you can quickly narrow down nearby options and walk into a store confident that you’ll leave with a device that truly eases your back tension.
+500 in_article https://im.runware.ai/image/ws/2/ii/6081a55a-3086-476c-8e06-22193716b559.webp /data/app/igny8/frontend/public/images/ai-images/image_500_1767226502.webp Close-up of a person thoughtfully comparing different types of back massagers on a coffee table: a handheld percussion massager, a shiatsu massage pillow, and a full back massage cushion. The person is holding a notepad with notes like 'upper back pain', 'budget', 'heat function'. Bright, neutral background, realistic style. generated 0 2026-01-01 00:11:13.441401+00 2026-01-01 00:11:13.441406+00 90 209 61 21 \N \N \N \N f \N Before you even type “back massager near me” into a search bar, it helps to define exactly what you’re looking for. Consider where your pain is located, how intense it feels, and whether you prefer deep kneading, gentle vibration, or targeted percussion so you can focus on products that match your specific needs.
+512 in_article https://im.runware.ai/image/ws/2/ii/d4d83955-f97e-454d-8487-955feeb860fe.webp /data/app/igny8/frontend/public/images/ai-images/image_512_1767226365.webp Wide view of a warehouse club like Costco or Sam’s Club, showing large massage chair displays alongside stacked pallets of electronics and home goods. A shopper tests a full-body massage chair while another examines a boxed back massager bundle. Spacious warehouse setting, high ceilings, natural and artificial lighting, realistic style. generated 2 2026-01-01 00:11:29.132182+00 2026-01-01 00:11:29.132193+00 90 211 61 21 \N \N \N \N f \N Big-box retailers and warehouse clubs near you can be ideal if you want value bundles or higher-end massage chairs. These stores may have fewer models than Walmart, but they often feature premium brands, in-store demos, and occasional instant rebates, making them a smart stop when you’re considering a larger investment in pain relief.
+511 in_article https://im.runware.ai/image/ws/2/ii/6531a5d1-e8d6-472a-b9e7-5129afb65eb4.webp /data/app/igny8/frontend/public/images/ai-images/image_511_1767226347.webp Interior of a Walmart health and personal care aisle, with shelves full of affordable back massagers: boxed shiatsu cushions, handheld percussion models, and heating pads. A shopper checks a product label while another uses the Walmart app on their phone. Bright, realistic lighting, clear Walmart branding, everyday-life photography style. generated 1 2026-01-01 00:11:29.125407+00 2026-01-01 00:11:29.125412+00 90 211 61 21 \N \N \N \N f \N Walmart often ranks high for back massagers because it combines wide coverage with budget-friendly pricing. Most locations carry multiple brands and styles, from entry-level handheld massagers to mid-range cushions with heat. You can compare features in person, check online reviews on your phone, and usually walk out with something the same day.
+509 featured https://im.runware.ai/image/ws/2/ii/388d681c-de56-4ae6-9b7d-69b47bf479a4.webp /data/app/igny8/frontend/public/images/ai-images/image_509_1767226329.webp Bright, modern retail aisle with various back massagers on shelves: handheld percussion massagers, shiatsu cushions, and compact massage chairs. A diverse adult couple compares boxes, checking prices and features. Clear store signage, warm lighting, clean layout. Realistic photography style, inviting and informative mood, slight depth of field emphasizing the massager section. generated 0 2026-01-01 00:11:29.114092+00 2026-01-01 00:11:29.114102+00 90 211 61 21 \N \N \N \N f \N When you start searching for a back massager nearby, you’ll usually find a surprising variety of options within a short drive. From simple handheld devices to mid-range cushions and premium massage chairs, understanding how different stores stock and price these products helps you match your pain relief needs to your budget and timeline.
+510 in_article https://im.runware.ai/image/ws/2/ii/a1d52e48-250f-4d55-8b21-5ed047d6009f.webp /data/app/igny8/frontend/public/images/ai-images/image_510_1767226309.webp Collage-style scene showing a comparison of four store types: a big-box retailer aisle, a warehouse club with pallets, a small wellness shop with display shelves, and a pharmacy counter. Icons or subtle labels indicate price, selection, and convenience. Clean, infographic-inspired layout, realistic elements, neutral background, clear and organized composition. generated 0 2026-01-01 00:11:29.119934+00 2026-01-01 00:11:29.119941+00 90 211 61 21 \N \N \N \N f \N To choose the best place to buy a back massager locally, it helps to compare store types side by side. Big-box chains, warehouse clubs, pharmacies, and specialty wellness shops each differ in selection, pricing consistency, and convenience. Knowing these trade-offs lets you quickly narrow down where to shop before you even leave home.
+477 in_article https://im.runware.ai/image/ws/2/ii/df8d1f0b-69e9-48d4-96cc-e59b0b9bdfee.webp /data/app/igny8/frontend/public/images/ai-images/image_477_1767226977.webp Side-by-side comparison layout: on the left, a slim handheld wand-style massager with a long handle reaching a person’s mid-back; on the right, a compact massage gun being used on the upper back and lats. Clean white background, subtle labels, modern product-photography style emphasizing ergonomic shapes and different head attachments. failed 0 2026-01-01 00:10:32.307133+00 2026-01-01 00:10:32.307145+00 90 204 61 21 \N \N \N \N f \N Handheld wands and massage guns may look similar, but they feel very different in use. Wands usually offer lighter, broader vibration and are easier for beginners or desk workers who want gentle relief. Massage guns deliver deeper percussion, better suited to athletes or anyone targeting dense tissue around the lats and spinal erectors.
+492 in_article https://im.runware.ai/image/ws/2/ii/878b64bf-16b2-4a36-99a6-dd385096c002.webp /data/app/igny8/frontend/public/images/ai-images/image_492_1767474869.webp Person standing in a bedroom using a long-handled heated back massager to reach the middle of their back, ergonomic curved handle clearly visible. Soft warm glow at the massage head suggests heat. Casual home setting with tidy bed and nightstand in background. Natural, lifestyle photography with emphasis on reach and everyday usability. generated 2 2026-01-01 00:10:56.588039+00 2026-01-01 00:10:56.588053+00 90 207 61 21 \N \N \N \N f \N For everyday use, a handheld back massager with a long, ergonomic handle can be a game changer, especially if you live alone or have limited shoulder mobility. The extended reach lets you target mid- and lower-back knots without awkward stretching, while built-in heat supports circulation and relaxation during short, frequent sessions throughout the week.
+494 featured https://im.runware.ai/image/ws/2/ii/a03cd83e-bea1-42be-89ba-ee6a1b1d4c4f.webp /data/app/igny8/frontend/public/images/ai-images/image_494_1767226613.webp Bright, modern retail store aisle focused on back massagers: neatly arranged shelves with various massagers (handheld, percussion guns, shiatsu cushions, chair pads) in colorful boxes. A shopper compares two boxes, checking labels for intensity levels, heat, and warranty. Natural lighting, clean design, realistic photography style, inviting and informative mood. generated 0 2026-01-01 00:11:05.887969+00 2026-01-01 00:11:05.887978+00 90 208 61 21 \N \N \N \N f \N Buying a back massager locally lets you see real devices instead of guessing from product photos. You can compare sizes, test weight and grip, and quickly scan packaging for features like heat, intensity settings, and warranty coverage. This hands-on experience makes it easier to narrow down options before you commit.
+495 in_article https://im.runware.ai/image/ws/2/ii/1bdcc9c6-bfb2-45fe-9c55-6dddb2362c8b.webp /data/app/igny8/frontend/public/images/ai-images/image_495_1767226594.webp Cozy wellness store interior with a small display of back massagers on a wooden table: premium shiatsu cushions, sleek percussion guns, and compact handheld models. A shopper talks with a staff member who gestures toward the products. Warm lighting, calm atmosphere, realistic style, emphasizing personal guidance and local shopping benefits. generated 0 2026-01-01 00:11:05.894004+00 2026-01-01 00:11:05.894009+00 90 208 61 21 \N \N \N \N f \N When you buy locally instead of online, you gain the chance to ask questions, feel build quality, and sometimes even test demo units. Staff at pharmacies or wellness boutiques can explain which models suit chronic tension versus occasional soreness, helping you avoid guesswork and costly returns that often come with online orders.
+503 in_article https://im.runware.ai/image/ws/2/ii/abf6fdf4-b756-4d0c-aecc-bce5bba65cef.webp /data/app/igny8/frontend/public/images/ai-images/image_503_1767226578.webp A person holding a smartphone while standing in a parking lot, map app open showing several nearby stores with star ratings, distances, and price tags for back massagers. In the background, a recognizable big-box store entrance and a smaller local shop. Golden hour lighting, realistic, everyday look. generated 3 2026-01-01 00:11:13.457784+00 2026-01-01 00:11:13.457789+00 90 209 61 21 \N \N \N \N f \N Once you’ve found a few options, compare nearby stores by distance, price, and reviews. A slightly longer drive might be worth it if the store has better-rated products or more knowledgeable staff. Balancing convenience with value helps you choose a place where you’re more likely to find a massager you’ll truly enjoy.
+476 featured https://im.runware.ai/image/ws/2/ii/d5022435-d25b-45ff-b3bd-a897a73a6a75.webp /data/app/igny8/frontend/public/images/ai-images/image_476_1767226993.webp A cozy modern living room scene with a person sitting comfortably in a soft armchair using a heated back massager on their upper back. Warm orange-red glow indicates heat. A massage gun and a handheld wand-style massager lie on a nearby coffee table. Soft evening lighting, clean minimalist decor, realistic photography style. failed 0 2026-01-01 00:10:32.289918+00 2026-01-01 00:10:32.289934+00 90 204 61 21 \N \N \N \N f \N Using a back massager with heat at home lets you tackle stiffness and stress on your own schedule, without last‑minute chiropractor visits. Modern devices combine targeted vibration or percussion with soothing warmth, helping muscles relax in under 15 minutes so you can recover from long workdays or intense workouts more efficiently.
+517 in_article https://im.runware.ai/image/ws/2/ii/755279ca-42ce-4e73-a32b-04d447d8a9a0.webp /data/app/igny8/frontend/public/images/ai-images/image_517_1767463437.webp Person lying semi-reclined in a chair using a heated back cushion, with a subtle overlay showing temperature zones along the spine. Warm orange-red gradients over the back area, calm facial expression, dim cozy lighting suggesting relaxation and pain relief. Realistic photography with slight infographic elements. generated 2 2026-01-03 17:52:34.656802+00 2026-01-03 17:52:34.656809+00 90 212 61 21 \N \N \N \N f \N Heat plays a central role in easing back pain and tension by relaxing tight muscle fibers and improving tissue elasticity. When combined with rhythmic massage, it can reduce stiffness, increase comfort during movement, and make stretching more effective, especially for people dealing with chronic low‑back tightness or long hours at a desk.
+514 featured https://im.runware.ai/image/ws/2/ii/c95a8ee2-7095-42b6-8fc1-a9d75f0b6582.webp /data/app/igny8/frontend/public/images/ai-images/image_514_1767463418.webp Modern home setting with a person sitting on a sofa using a heated back massager on their lower back. Visible soft red/orange glow indicating heat, relaxed posture, casual clothing. Clean, bright, wellness-focused environment with a laptop and water bottle on a side table. Realistic photography style, warm lighting, calm mood. generated 0 2026-01-03 17:52:34.635205+00 2026-01-03 17:52:34.635214+00 90 212 61 21 \N \N \N \N f \N A quality heated back massager turns your living room into a personal recovery space, easing desk-related stiffness and post‑workout soreness without appointments. By combining mechanical pressure with gentle warmth, it boosts local circulation, helps clear metabolic waste, and reduces that guarded, locked‑up feeling that often builds across the lower and mid back.
+518 in_article https://im.runware.ai/image/ws/2/ii/6dd369a2-162c-40c8-8ba5-2c40c63b7f0f.webp /data/app/igny8/frontend/public/images/ai-images/image_518_1767463443.webp Flat-lay arrangement of different heated back massagers: handheld wand, massage gun, heated wrap, and a shiatsu back cushion. Each device neatly arranged on a neutral background with subtle glow effects indicating heat. Clean, modern, product-comparison aesthetic, overhead view, soft natural lighting. generated 3 2026-01-03 17:52:34.664251+00 2026-01-03 17:52:34.664259+00 90 212 61 21 \N \N \N \N f \N Today’s market offers several types of heated back massagers, from long-handled wands and powerful massage guns to wraparound belts and shiatsu cushions for chairs. Each style varies in reach, intensity, and heat coverage, so choosing the right design depends on whether you prioritize portability, deep tissue work, or hands‑free, full‑back comfort.
+516 in_article https://im.runware.ai/image/ws/2/ii/0ad0ae2b-5bcd-422b-8b82-a27de4fdf108.webp /data/app/igny8/frontend/public/images/ai-images/image_516_1767463427.webp Side-by-side comparison layout: on the left, a classic handheld wand-style back massager with a long handle; on the right, a compact massage gun with multiple attachments. Clean white background, subtle labels, soft shadows, product-style photography emphasizing ergonomic shapes and controls. generated 1 2026-01-03 17:52:34.650085+00 2026-01-03 17:52:34.650098+00 90 212 61 21 \N \N \N \N f \N Handheld wands and massage guns both target back tension, but they deliver force differently. A wand-style back massager excels at broad, sweeping strokes and easier reach to the mid and lower back, while a massage gun focuses more percussive force into smaller areas, ideal for dense knots and post‑workout trigger points.
+515 in_article https://im.runware.ai/image/ws/2/ii/1953137e-e572-4e06-8936-88fe4d355b23.webp /data/app/igny8/frontend/public/images/ai-images/image_515_1767463405.webp Close-up of a heated back massager applied to the lumbar area over clothing, with a transparent overlay showing muscles and increased blood flow in red tones. Soft orange heat glow from the device. Educational, medical-illustration style blended with realistic photography, neutral background, clear focus on how heat penetrates muscle tissue. generated 0 2026-01-03 17:52:34.643204+00 2026-01-03 17:52:34.64321+00 90 212 61 21 \N \N \N \N f \N A heated back massager works by delivering both mechanical pressure and therapeutic warmth to the deeper layers of back muscles. As tissues warm, blood vessels dilate and circulation can increase by as much as 20–25%, which supports nutrient delivery, waste removal, and easing the rigid muscle guarding that makes your back feel chronically tight.
+524 featured https://oaidalleapiprodscus.blob.core.windows.net/private/org-d6y6V9UCfxJGPrrw8U9X7iX2/user-Hcrg9zi6BPbsvyJfu4qZ5wyy/img-5ltf78WDwTuU4Dz59TrqZL8S.png?st=2026-01-03T21%3A28%3A31Z&se=2026-01-03T23%3A28%3A31Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=0ec745ad-3d9b-4d43-86db-87a2c9b420f1&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2026-01-03T20%3A00%3A18Z&ske=2026-01-04T20%3A00%3A18Z&sks=b&skv=2024-08-04&sig=s0lpcPQcpBVPe8HmXnPSoiRI/fwXXKtlLYvc%2BdBdGi8%3D /data/app/igny8/frontend/public/images/ai-images/image_524_1767479311.png Bright, modern Walmart store interior showing a shopper comparing several back massagers on a neatly organized shelf. Visible boxes for handheld, shiatsu cushion, and massage chair toppers with clear price tags and rollback signs. Warm, inviting lighting, realistic photography style, slightly elevated angle, diverse shoppers in background, clean and budget-friendly atmosphere. generated 0 2026-01-03 18:14:28.811293+00 2026-01-03 18:14:28.811304+00 90 210 61 21 \N \N \N \N f \N When you search for a back massager at Walmart, you’ll usually see a wide price range and plenty of options on the shelf. From compact handheld units to larger shiatsu cushions, the variety makes it easier to match your budget and comfort preferences while taking advantage of Walmart’s frequent rollbacks and promotions.
+525 in_article https://oaidalleapiprodscus.blob.core.windows.net/private/org-d6y6V9UCfxJGPrrw8U9X7iX2/user-Hcrg9zi6BPbsvyJfu4qZ5wyy/img-iaKaRK6t207U8eA9O0z1NlbO.png?st=2026-01-03T21%3A28%3A50Z&se=2026-01-03T23%3A28%3A50Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=1726b4ce-fee1-450b-8b92-1731ad8745f6&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2026-01-03T22%3A28%3A50Z&ske=2026-01-04T22%3A28%3A50Z&sks=b&skv=2024-08-04&sig=1GqSdQaOBo9vJdU54nhLxP6KUcr%2B9d%2BxWhuTxgXGE24%3D /data/app/igny8/frontend/public/images/ai-images/image_525_1767479331.png Wide shot of a Walmart health and wellness aisle with a prominent back massager section. Shelves display multiple brands and models, with rollback and clearance tags visible. A shopper holds a box, checking features on the back. Realistic style, bright lighting, clean layout, emphasizing affordability and choice. generated 0 2026-01-03 18:14:28.819937+00 2026-01-03 18:14:28.819958+00 90 210 61 21 \N \N \N \N f \N Choosing Walmart for a back massager purchase often comes down to convenience and value. With dedicated sections in many stores, you can quickly compare brands, read feature lists on the packaging, and see current rollback prices. This in-person comparison helps you avoid overpaying for features you may not actually use at home.
+527 in_article https://oaidalleapiprodscus.blob.core.windows.net/private/org-d6y6V9UCfxJGPrrw8U9X7iX2/user-Hcrg9zi6BPbsvyJfu4qZ5wyy/img-yNmQf1icAjYhP6io6gW6SySo.png?st=2026-01-03T21%3A29%3A34Z&se=2026-01-03T23%3A29%3A34Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=92b997d6-f0dd-439d-8087-b26c74d72365&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2026-01-03T06%3A21%3A02Z&ske=2026-01-04T06%3A21%3A02Z&sks=b&skv=2024-08-04&sig=iRPmfFuWDAdAtvejbk5V8pX91H8JLGhJp/sA9WlY2nM%3D /data/app/igny8/frontend/public/images/ai-images/image_527_1767479374.png Laptop and smartphone on a kitchen table showing Walmart’s website and app with a search for “back massager.” Filters for price, pickup today, and brand are visible. A hand taps the ‘Pickup today’ option. Realistic, bright home setting, casual mood, focus on screens and interface. generated 2 2026-01-03 18:14:28.835716+00 2026-01-03 18:14:28.835724+00 90 210 61 21 \N \N \N \N f \N Before heading to the store, checking Walmart’s website or app for back massager availability can save time. Using filters like “Pickup today,” price range, and customer ratings helps you narrow options to what’s actually in stock locally, so you can reserve a unit online and avoid hunting through multiple aisles in person.
+526 in_article https://oaidalleapiprodscus.blob.core.windows.net/private/org-d6y6V9UCfxJGPrrw8U9X7iX2/user-Hcrg9zi6BPbsvyJfu4qZ5wyy/img-c2N9oPQchAo5d003U2o7hHTb.png?st=2026-01-03T21%3A29%3A13Z&se=2026-01-03T23%3A29%3A13Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=75dd98d5-7f83-456f-ae8d-a58a7fba1b61&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2026-01-03T21%3A27%3A35Z&ske=2026-01-04T21%3A27%3A35Z&sks=b&skv=2024-08-04&sig=wUHUqfs7PchtMLhZzmodRVBfkpQa9JvMyUd4SRHtteI%3D /data/app/igny8/frontend/public/images/ai-images/image_526_1767479353.png Close-up display of different types of back massagers at Walmart: a compact handheld percussion massager, a shiatsu back cushion with heat, a massage gun, and a larger massage seat topper. Each product is clearly labeled with price and key features. Realistic product photography, neutral background, sharp focus. generated 1 2026-01-03 18:14:28.82721+00 2026-01-03 18:14:28.827217+00 90 210 61 21 \N \N \N \N f \N In a single Walmart aisle, you’ll typically find several massager types: handheld units for targeted knots, shiatsu cushions that mimic kneading hands, massage guns for deep muscle work, and seat toppers that cover the full back. Understanding these categories makes it easier to match a device to your specific pain points and routine.
+523 in_article https://oaidalleapiprodscus.blob.core.windows.net/private/org-d6y6V9UCfxJGPrrw8U9X7iX2/user-Hcrg9zi6BPbsvyJfu4qZ5wyy/img-GNTw5h5FTjAXKwTyyigbT36g.png?st=2026-01-03T20%3A57%3A41Z&se=2026-01-03T22%3A57%3A41Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=7aed557a-269d-4dda-ab8b-c66e34024151&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2026-01-03T21%3A57%3A41Z&ske=2026-01-04T21%3A57%3A41Z&sks=b&skv=2024-08-04&sig=an8xRD/Yxiai4bl4z8CNC/JZLZjtBLCEm7x32NNWdJ4%3D /data/app/igny8/frontend/public/images/ai-images/image_523_1767477462.png A person lounging on a sofa using a heated handheld back massager, with a subtle orange glow indicating warmth on the lower back. Next to them, a massage gun without visible heat function sits on a side table. Soft, relaxing evening lighting, home wellness atmosphere, realistic photography. generated 3 2026-01-03 18:10:26.09425+00 2026-01-03 18:10:26.094256+00 90 206 61 21 \N \N \N \N f \N Heat is where many handheld designs stand out. Built-in warming elements can boost blood flow and help muscles relax more quickly, which feels especially soothing at the end of a long day. Most massage guns skip heat and instead focus on stronger mechanical impact, trading cozy comfort for more intense muscle work.
+522 in_article https://im.runware.ai/image/ws/2/ii/8a851d37-2025-422d-b405-a2b7e32617f1.webp /data/app/igny8/frontend/public/images/ai-images/image_522_1767475251.webp Side-by-side illustration-style image showing a handheld massager applying shallow, wide vibrations to the back muscles, and a massage gun delivering deeper, focused percussive strokes into muscle tissue. Cross-section style with simplified muscles, clean medical infographic look, soft blues and neutrals. failed 2 2026-01-03 18:10:26.087902+00 2026-01-03 18:10:26.087915+00 90 206 61 21 \N \N \N \N f \N The main difference between these devices is how they deliver pressure into your muscles. Handheld models usually provide broader, surface-level vibration that gently relaxes tight areas, while massage guns drive more concentrated, rhythmic percussive force deeper into tissue, better suited for dense knots and post-workout soreness.
+519 featured https://im.runware.ai/image/ws/2/ii/581daeb7-00f8-4519-ae33-feed7e247b56.webp /data/app/igny8/frontend/public/images/ai-images/image_519_1767475217.webp A split-screen comparison of a person using a sleek cordless massage gun on the upper back on one side, and another person using a curved handheld back massager with heat on the lower back on the other side. Bright, modern home setting, neutral colors, clear focus on the two device styles, clean editorial style photography. failed 0 2026-01-03 18:10:26.061495+00 2026-01-03 18:10:26.06151+00 90 206 61 21 \N \N \N \N f \N Choosing between a handheld back massager and a massage gun starts with understanding how your pain feels and where it lives. Surface-level tightness from desk work usually responds well to gentler vibration and heat, while deep, stubborn knots from intense training often need the stronger percussive force of a massage gun.
+521 in_article https://im.runware.ai/image/ws/2/ii/a8d5dc8a-3190-4156-bca7-a8753a431690.webp /data/app/igny8/frontend/public/images/ai-images/image_521_1767475240.webp A compact, cordless massage gun with multiple attachment heads laid out on a gym bench beside a water bottle and towel. An athletic person in workout clothes picks up the gun, locker-room or gym background slightly blurred, cool and energetic lighting, modern fitness product photography. failed 1 2026-01-03 18:10:26.078027+00 2026-01-03 18:10:26.078043+00 90 206 61 21 \N \N \N \N f \N Massage guns are designed with athletes and active users in mind, pairing a compact body with interchangeable heads that target different muscle groups. Their cordless design and higher-intensity percussion make them easy to toss in a gym bag and use before or after workouts to address deep muscle fatigue.
+\.
+
+
+--
+-- Data for Name: igny8_industries; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_industries (id, name, slug, description, is_active, created_at, updated_at) FROM stdin;
+21 Home & Garden home-garden Home improvement, gardening, landscaping, and interior design t 2025-11-05 11:07:20.33184+00 2025-11-05 11:07:20.331857+00
+22 Apparel & Fashion apparel-fashion Fashion, clothing, and apparel industry t 2025-11-19 20:57:22.128761+00 2025-11-19 20:57:22.128785+00
+23 Beauty & Personal Care beauty-personal-care Beauty, skincare, and personal care products and services t 2025-11-19 20:57:22.140996+00 2025-11-19 20:57:22.141002+00
+9 Automotive automotive Automotive sales, services, and parts t 2025-11-04 16:43:57.02681+00 2025-11-19 20:57:22.146579+00
+24 Home & Furniture home-furniture Furniture, home decor, and home improvement t 2025-11-19 20:57:22.155198+00 2025-11-19 20:57:22.155219+00
+25 Healthcare & Medical healthcare-medical Healthcare services, medical practices, and health-related services t 2025-11-19 20:57:22.161659+00 2025-11-19 20:57:22.161668+00
+26 Real Estate & Construction real-estate-construction Real estate, property management, and construction services t 2025-11-19 20:57:22.168532+00 2025-11-19 20:57:22.168538+00
+27 Technology & IT Services technology-it-services Technology services, software development, and IT solutions t 2025-11-19 20:57:22.175035+00 2025-11-19 20:57:22.175044+00
+28 Finance & Insurance finance-insurance Financial services, banking, insurance, and investment t 2025-11-19 20:57:22.182419+00 2025-11-19 20:57:22.182436+00
+29 Education & Training education-training Educational institutions, training programs, and learning services t 2025-11-19 20:57:22.190068+00 2025-11-19 20:57:22.190076+00
+7 Food & Beverage food-beverage Restaurants, food services, and beverage industry t 2025-11-04 16:43:57.006837+00 2025-11-19 20:57:22.196169+00
+1 Technology technology Software, cloud computing, cybersecurity, and technology services f 2025-11-04 14:56:39.099439+00 2025-11-19 21:05:19.931662+00
+10 Fashion & Apparel fashion-apparel Fashion, clothing, and apparel businesses f 2025-11-04 16:43:57.035905+00 2025-11-19 21:05:19.935264+00
+\.
+
+
+--
+-- Data for Name: igny8_industry_sectors; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_industry_sectors (id, name, slug, description, suggested_keywords, is_active, created_at, updated_at, industry_id) FROM stdin;
+3 Cybersecurity cybersecurity Security services, threat protection, and compliance ["cybersecurity services", "network security", "data protection", "security audit", "penetration testing", "managed security services", "compliance consulting", "security monitoring", "incident response", "security training"] f 2025-11-04 14:56:39.110999+00 2025-11-04 16:44:55.595256+00 1
+5 IT Consulting IT-consulting IT strategy, implementation, and support services ["IT consulting", "IT strategy", "digital transformation", "IT infrastructure", "network setup", "IT support services", "system integration", "IT project management", "technology assessment", "IT outsourcing"] f 2025-11-04 16:43:56.954125+00 2025-11-04 16:44:55.598454+00 1
+31 Auto Parts auto-parts Automotive parts and accessories ["auto parts", "car parts", "automotive parts", "car accessories", "auto parts store", "car parts online", "replacement parts", "aftermarket parts", "OEM parts", "auto supplies"] t 2025-11-04 16:43:57.03431+00 2025-11-04 16:44:55.646738+00 9
+32 Clothing Stores clothing-stores Clothing and apparel retail ["clothing store", "fashion store", "apparel store", "online clothing", "fashion retail", "clothing boutique", "fashion brand", "clothing online", "fashion shop", "apparel shop"] t 2025-11-04 16:43:57.038132+00 2025-11-04 16:44:55.649202+00 10
+34 Accessories accessories Fashion accessories and jewelry ["fashion accessories", "jewelry", "handbags", "watches", "sunglasses", "accessories store", "fashion jewelry", "designer accessories", "accessories online", "fashion accessories shop"] t 2025-11-04 16:43:57.042827+00 2025-11-04 16:44:55.65198+00 10
+30 Auto Repair auto-repair Auto repair and maintenance services ["auto repair", "car repair", "auto mechanic", "car maintenance", "auto service", "vehicle repair", "car diagnostics", "engine repair", "brake repair", "auto shop"] f 2025-11-04 16:43:57.032463+00 2025-11-19 20:57:22.153182+00 9
+29 Car Dealerships car-dealerships Car sales and dealership services ["car dealership", "car sales", "new car dealer", "used car dealer", "car buying", "car financing", "automotive dealer", "car showroom", "vehicle sales", "car trade-in"] f 2025-11-04 16:43:57.029156+00 2025-11-19 20:57:22.154138+00 9
+33 Fashion Design fashion-design Fashion design and custom clothing ["fashion design", "custom clothing", "fashion designer", "bespoke tailoring", "custom suits", "fashion consulting", "clothing design", "fashion styling", "wardrobe consulting", "personal styling"] t 2025-11-04 16:43:57.040655+00 2025-11-04 16:44:55.650486+00 10
+1 Software Development software-development Custom software development, SaaS products, and applications ["custom software development", "SaaS development", "web application development", "mobile app development", "software consulting", "enterprise software", "cloud software", "API development", "software architecture", "agile development"] f 2025-11-04 14:56:39.104761+00 2025-11-04 16:44:55.59184+00 1
+59 Gardening gardening Plants, flowers, vegetables, and garden maintenance ["organic gardening", "vegetable gardening", "flower garden design", "garden tools", "plant care tips", "composting guide", "garden pest control", "herb garden ideas", "garden irrigation systems", "seasonal planting guide"] t 2025-11-05 11:07:20.335388+00 2025-11-05 11:07:20.335405+00 21
+60 Home Improvement home-improvement DIY projects, renovations, and home repairs ["home renovation ideas", "diy home projects", "kitchen remodeling", "bathroom renovation", "flooring installation", "painting tips", "home repair guide", "power tools review", "home maintenance checklist", "interior design trends"] t 2025-11-05 11:07:20.337647+00 2025-11-05 11:07:20.337657+00 21
+61 Landscaping landscaping Outdoor design, lawn care, and hardscaping ["landscape design ideas", "lawn care tips", "outdoor patio design", "deck building guide", "garden pathways", "outdoor lighting ideas", "lawn mowing tips", "tree planting guide", "outdoor kitchen design", "garden edging ideas"] t 2025-11-05 11:07:20.339048+00 2025-11-05 11:07:20.339055+00 21
+62 Interior Design interior-design Home decoration, furniture, and interior styling ["interior design styles", "home decor ideas", "furniture arrangement", "color scheme ideas", "small space design", "home staging tips", "decoration trends", "room makeover ideas", "interior lighting design", "home organization tips"] t 2025-11-05 11:07:20.34049+00 2025-11-05 11:07:20.340496+00 21
+63 Home Decor home-decor Decorative items, accessories, and home styling ["home decor accessories", "wall art ideas", "curtain design tips", "pillow arrangement", "vase decoration ideas", "home fragrance tips", "decorative mirrors", "rug selection guide", "home accent pieces", "seasonal home decor"] t 2025-11-05 11:07:20.342147+00 2025-11-05 11:07:20.342152+00 21
+64 Menswear menswear \N [] t 2025-11-19 20:57:22.132987+00 2025-11-19 20:57:22.132996+00 22
+65 Womenswear womenswear \N [] t 2025-11-19 20:57:22.136169+00 2025-11-19 20:57:22.136175+00 22
+66 Kidswear kidswear \N [] t 2025-11-19 20:57:22.136646+00 2025-11-19 20:57:22.136655+00 22
+67 Sportswear & Activewear sportswear-activewear \N [] t 2025-11-19 20:57:22.137057+00 2025-11-19 20:57:22.137063+00 22
+68 Footwear footwear \N [] t 2025-11-19 20:57:22.137513+00 2025-11-19 20:57:22.137521+00 22
+69 Accessories accessories \N [] t 2025-11-19 20:57:22.13793+00 2025-11-19 20:57:22.137934+00 22
+70 Uniforms & Workwear uniforms-workwear \N [] t 2025-11-19 20:57:22.138322+00 2025-11-19 20:57:22.138327+00 22
+71 Luxury Fashion luxury-fashion \N [] t 2025-11-19 20:57:22.138696+00 2025-11-19 20:57:22.1387+00 22
+72 Ethnic & Cultural Wear ethnic-cultural-wear \N [] t 2025-11-19 20:57:22.139039+00 2025-11-19 20:57:22.139044+00 22
+73 Skincare skincare \N [] t 2025-11-19 20:57:22.142315+00 2025-11-19 20:57:22.142321+00 23
+74 Haircare haircare \N [] t 2025-11-19 20:57:22.142713+00 2025-11-19 20:57:22.142718+00 23
+75 Makeup & Cosmetics makeup-cosmetics \N [] t 2025-11-19 20:57:22.143064+00 2025-11-19 20:57:22.14307+00 23
+76 Fragrances fragrances \N [] t 2025-11-19 20:57:22.143494+00 2025-11-19 20:57:22.143499+00 23
+77 Personal Hygiene personal-hygiene \N [] t 2025-11-19 20:57:22.14387+00 2025-11-19 20:57:22.143877+00 23
+78 Spa & Salon Services spa-salon-services \N [] t 2025-11-19 20:57:22.144196+00 2025-11-19 20:57:22.144201+00 23
+79 Dermatology Clinics dermatology-clinics \N [] t 2025-11-19 20:57:22.144598+00 2025-11-19 20:57:22.144602+00 23
+80 Beauty Devices & Tools beauty-devices-tools \N [] t 2025-11-19 20:57:22.144939+00 2025-11-19 20:57:22.144943+00 23
+81 New Car Sales new-car-sales \N [] t 2025-11-19 20:57:22.14824+00 2025-11-19 20:57:22.148246+00 9
+82 Used Car Sales used-car-sales \N [] t 2025-11-19 20:57:22.148615+00 2025-11-19 20:57:22.148621+00 9
+83 Auto Parts & Accessories auto-parts-accessories \N [] t 2025-11-19 20:57:22.148936+00 2025-11-19 20:57:22.14894+00 9
+84 Auto Repair & Maintenance auto-repair-maintenance \N [] t 2025-11-19 20:57:22.149286+00 2025-11-19 20:57:22.149291+00 9
+85 Car Detailing car-detailing \N [] t 2025-11-19 20:57:22.149665+00 2025-11-19 20:57:22.149671+00 9
+86 Car Rental & Leasing car-rental-leasing \N [] t 2025-11-19 20:57:22.149969+00 2025-11-19 20:57:22.149973+00 9
+87 Electric Vehicles electric-vehicles \N [] t 2025-11-19 20:57:22.150345+00 2025-11-19 20:57:22.150351+00 9
+88 Tires & Wheels tires-wheels \N [] t 2025-11-19 20:57:22.150716+00 2025-11-19 20:57:22.15072+00 9
+89 Motorcycles & Bikes motorcycles-bikes \N [] t 2025-11-19 20:57:22.151001+00 2025-11-19 20:57:22.151005+00 9
+90 Furniture furniture \N [] t 2025-11-19 20:57:22.156673+00 2025-11-19 20:57:22.156679+00 24
+91 Home Decor home-decor \N [] t 2025-11-19 20:57:22.157053+00 2025-11-19 20:57:22.157059+00 24
+92 Bedding & Mattresses bedding-mattresses \N [] t 2025-11-19 20:57:22.157463+00 2025-11-19 20:57:22.157468+00 24
+93 Kitchen & Dining kitchen-dining \N [] t 2025-11-19 20:57:22.157783+00 2025-11-19 20:57:22.157787+00 24
+94 Home Improvement & Renovation home-improvement-renovation \N [] t 2025-11-19 20:57:22.15808+00 2025-11-19 20:57:22.158084+00 24
+95 Lighting lighting \N [] t 2025-11-19 20:57:22.158476+00 2025-11-19 20:57:22.158482+00 24
+96 Storage & Organization storage-organization \N [] t 2025-11-19 20:57:22.158903+00 2025-11-19 20:57:22.158909+00 24
+97 Outdoor Furniture outdoor-furniture \N [] t 2025-11-19 20:57:22.159286+00 2025-11-19 20:57:22.159291+00 24
+98 Interior Design Services interior-design-services \N [] t 2025-11-19 20:57:22.159637+00 2025-11-19 20:57:22.159641+00 24
+99 Clinics & General Practice clinics-general-practice \N [] t 2025-11-19 20:57:22.16309+00 2025-11-19 20:57:22.163098+00 25
+100 Dentistry dentistry \N [] t 2025-11-19 20:57:22.163678+00 2025-11-19 20:57:22.163685+00 25
+101 Physiotherapy & Rehabilitation physiotherapy-rehabilitation \N [] t 2025-11-19 20:57:22.164045+00 2025-11-19 20:57:22.164051+00 25
+102 Hospitals & Diagnostic Centers hospitals-diagnostic-centers \N [] t 2025-11-19 20:57:22.164891+00 2025-11-19 20:57:22.164896+00 25
+103 Mental Health & Therapy mental-health-therapy \N [] t 2025-11-19 20:57:22.165308+00 2025-11-19 20:57:22.165314+00 25
+104 Nutrition & Dietetics nutrition-dietetics \N [] t 2025-11-19 20:57:22.165719+00 2025-11-19 20:57:22.165725+00 25
+105 Medical Equipment & Supplies medical-equipment-supplies \N [] t 2025-11-19 20:57:22.166106+00 2025-11-19 20:57:22.166111+00 25
+106 Alternative Medicine alternative-medicine \N [] t 2025-11-19 20:57:22.166502+00 2025-11-19 20:57:22.166507+00 25
+107 Residential Real Estate residential-real-estate \N [] t 2025-11-19 20:57:22.170014+00 2025-11-19 20:57:22.170019+00 26
+108 Commercial Real Estate commercial-real-estate \N [] t 2025-11-19 20:57:22.170465+00 2025-11-19 20:57:22.17047+00 26
+109 Real Estate Agencies real-estate-agencies \N [] t 2025-11-19 20:57:22.170833+00 2025-11-19 20:57:22.170837+00 26
+110 Property Management property-management \N [] t 2025-11-19 20:57:22.17136+00 2025-11-19 20:57:22.171365+00 26
+111 Construction & Contracting construction-contracting \N [] t 2025-11-19 20:57:22.171788+00 2025-11-19 20:57:22.171794+00 26
+112 Architecture & Interior Design architecture-interior-design \N [] t 2025-11-19 20:57:22.172174+00 2025-11-19 20:57:22.172179+00 26
+113 Home Inspection home-inspection \N [] t 2025-11-19 20:57:22.172598+00 2025-11-19 20:57:22.172605+00 26
+114 Real Estate Investment real-estate-investment \N [] t 2025-11-19 20:57:22.172992+00 2025-11-19 20:57:22.172997+00 26
+115 Software Development software-development \N [] t 2025-11-19 20:57:22.176432+00 2025-11-19 20:57:22.176437+00 27
+116 IT Support & Managed Services it-support-managed-services \N [] t 2025-11-19 20:57:22.177092+00 2025-11-19 20:57:22.177098+00 27
+117 Cybersecurity cybersecurity \N [] t 2025-11-19 20:57:22.177582+00 2025-11-19 20:57:22.177589+00 27
+118 Web Development & Design web-development-design \N [] t 2025-11-19 20:57:22.177966+00 2025-11-19 20:57:22.17797+00 27
+119 SaaS Products saas-products \N [] t 2025-11-19 20:57:22.178409+00 2025-11-19 20:57:22.178415+00 27
+120 Cloud Services cloud-services \N [] t 2025-11-19 20:57:22.178848+00 2025-11-19 20:57:22.178854+00 27
+121 Data & AI Services data-ai-services \N [] t 2025-11-19 20:57:22.17928+00 2025-11-19 20:57:22.179286+00 27
+122 Digital Marketing Agencies digital-marketing-agencies \N [] t 2025-11-19 20:57:22.179694+00 2025-11-19 20:57:22.1797+00 27
+123 Banking banking \N [] t 2025-11-19 20:57:22.184486+00 2025-11-19 20:57:22.184497+00 28
+124 Loans & Lending loans-lending \N [] t 2025-11-19 20:57:22.18534+00 2025-11-19 20:57:22.185347+00 28
+125 Insurance insurance \N [] t 2025-11-19 20:57:22.185742+00 2025-11-19 20:57:22.185747+00 28
+126 Accounting & Tax Services accounting-tax-services \N [] t 2025-11-19 20:57:22.186268+00 2025-11-19 20:57:22.186274+00 28
+127 Investment & Wealth Management investment-wealth-management \N [] t 2025-11-19 20:57:22.1867+00 2025-11-19 20:57:22.186706+00 28
+128 Fintech Services fintech-services \N [] t 2025-11-19 20:57:22.187129+00 2025-11-19 20:57:22.187135+00 28
+129 Credit Repair credit-repair \N [] t 2025-11-19 20:57:22.187574+00 2025-11-19 20:57:22.187579+00 28
+130 Mortgage Brokers mortgage-brokers \N [] t 2025-11-19 20:57:22.18795+00 2025-11-19 20:57:22.187956+00 28
+131 Schools & Colleges schools-colleges \N [] t 2025-11-19 20:57:22.191549+00 2025-11-19 20:57:22.191556+00 29
+132 Test Preparation test-preparation \N [] t 2025-11-19 20:57:22.191927+00 2025-11-19 20:57:22.191933+00 29
+133 Skill Development Courses skill-development-courses \N [] t 2025-11-19 20:57:22.192308+00 2025-11-19 20:57:22.192314+00 29
+134 Coaching & Tutoring coaching-tutoring \N [] t 2025-11-19 20:57:22.192721+00 2025-11-19 20:57:22.192727+00 29
+135 Online Learning Platforms online-learning-platforms \N [] t 2025-11-19 20:57:22.193069+00 2025-11-19 20:57:22.193075+00 29
+136 Professional Certifications professional-certifications \N [] t 2025-11-19 20:57:22.193493+00 2025-11-19 20:57:22.193499+00 29
+137 University Programs university-programs \N [] t 2025-11-19 20:57:22.19386+00 2025-11-19 20:57:22.193866+00 29
+138 Corporate Training corporate-training \N [] t 2025-11-19 20:57:22.194297+00 2025-11-19 20:57:22.194303+00 29
+23 Restaurants restaurants Restaurants, cafes, and dining establishments ["restaurant", "fine dining", "casual dining", "cafe", "food delivery", "takeout", "restaurant reservations", "catering services", "brunch restaurant", "dinner restaurant"] t 2025-11-04 16:43:57.008684+00 2025-11-19 20:57:22.198296+00 7
+139 Cafes & Bakeries cafes-bakeries \N [] t 2025-11-19 20:57:22.199457+00 2025-11-19 20:57:22.199464+00 7
+24 Food Delivery food-delivery Food delivery services and meal kits ["food delivery", "meal delivery", "food delivery service", "online food ordering", "food delivery app", "meal kit delivery", "grocery delivery", "food delivery platform", "restaurant delivery", "fast food delivery"] t 2025-11-04 16:43:57.011893+00 2025-11-19 20:57:22.199878+00 7
+140 Packaged Foods packaged-foods \N [] t 2025-11-19 20:57:22.200562+00 2025-11-19 20:57:22.200569+00 7
+141 Organic & Health Foods organic-health-foods \N [] t 2025-11-19 20:57:22.201+00 2025-11-19 20:57:22.201006+00 7
+142 Grocery Stores grocery-stores \N [] t 2025-11-19 20:57:22.201497+00 2025-11-19 20:57:22.201503+00 7
+143 Catering Services catering-services \N [] t 2025-11-19 20:57:22.201863+00 2025-11-19 20:57:22.201869+00 7
+144 Beverage Companies beverage-companies \N [] t 2025-11-19 20:57:22.202229+00 2025-11-19 20:57:22.202235+00 7
+25 Catering catering Catering services for events and businesses ["catering services", "event catering", "wedding catering", "corporate catering", "party catering", "catering company", "catered events", "catering menu", "buffet catering", "full service catering"] f 2025-11-04 16:43:57.014812+00 2025-11-19 20:57:22.204414+00 7
+\.
+
+
+--
+-- Data for Name: igny8_integration_providers; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_integration_providers (provider_id, display_name, provider_type, api_key, api_secret, webhook_secret, api_endpoint, config, is_active, is_sandbox, created_at, updated_at, updated_by_id) FROM stdin;
+anthropic Anthropic (Claude) ai {"default_model": "claude-3-5-sonnet-20241022"} f f 2026-01-04 06:13:55.112341+00 2026-01-04 06:13:55.112386+00 \N
+google Google Cloud ai {} f f 2026-01-04 06:13:55.114457+00 2026-01-04 06:13:55.114467+00 \N
+cloudflare_r2 Cloudflare R2 storage {"bucket": "", "endpoint": ""} f f 2026-01-04 06:13:55.121457+00 2026-01-04 06:13:55.121463+00 \N
+openai OpenAI ai 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 {"models": ["gpt-4o-mini", "gpt-4o", "gpt-5.1", "dall-e-3"], "default_model": "gpt-5.1"} t f 2026-01-04 06:13:55.106688+00 2026-01-04 07:06:47.425654+00 \N
+runware Runware ai tuHmZhhyUcArJUQ3r0Jiw8ViPaiit0Z3 {"models": ["runware:97@1", "google:4@2"], "default_model": "runware:97@1"} t f 2026-01-04 06:13:55.109736+00 2026-01-04 07:06:47.428628+00 \N
+stripe Stripe (Credit/Debit Cards) payment pk_test_51SlRuFPdRe4dWeLwCUor4IyU8Y8q7JzgUpp74AbQe5IoX7pcLMn0HwoRVptbQtgLd6kYxuQx3MC4e9MMVzEDhXPr00ZIDRlpCy sk_test_51SlRuFPdRe4dWeLw3nAw7wWOatE88Yx8ZISkDkYeMP3KA5mdVqtT1czQ25tvWQgL8QB3paQlFwPn6hBtVwWUkBnB00DIFzCniy whsec_6tEBsvfPSlaGgWtTcvapI9HP1WxxjCFW https://api.igny8.com/api/v1/billing/webhooks/stripe/ {"currency": "usd", "payment_methods": ["card"], "billing_portal_enabled": true} t t 2026-01-04 06:13:55.116174+00 2026-01-07 03:17:20.088589+00 3
+paypal PayPal payment AZ7CAmfyyKXSBmifY-kWqUYc4xNSu4mt5GGO_yL1_e5TdbHMTljMbhDc9LpHyvkgxC_wgE_OTJ32E-sl EHNxohO1p0cJMECpg4FZWLWJd377bE5NJ9z1Lm28e6yiEfFcfTgQOez6HMKB0HKpLF239DB-RMa42vF4 1C216352890918202 https://api-m.sandbox.paypal.com {"currency": "USD", "cancel_url": "https://app.igny8.com/account/plans?paypal=cancel", "return_url": "https://app.igny8.com/account/plans?paypal=success", "webhook_id": "WH-xxxxxxxxxxxxx"} t t 2026-01-04 06:13:55.117939+00 2026-01-07 07:31:26.928664+00 3
+resend Resend email re_Z4WHNYUM_8wSGnAYtC2YroPSTg7Y3u2Zu {"reply_to": "support@igny8.com", "from_name": "IGNY8", "from_email": "noreply@igny8.com"} t f 2026-01-04 06:13:55.119568+00 2026-01-08 01:09:02.096293+00 3
+\.
+
+
+--
+-- Data for Name: igny8_integration_settings; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_integration_settings (id, integration_type, config, is_active, updated_at, created_at, tenant_id) FROM stdin;
+\.
+
+
+--
+-- Data for Name: igny8_invoices; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_invoices (id, invoice_number, subtotal, tax, total, status, invoice_date, due_date, paid_at, line_items, stripe_invoice_id, payment_method, notes, metadata, created_at, updated_at, tenant_id, currency, subscription_id) FROM stdin;
+11 INV-90-202512-0001 139.00 0.00 139.00 paid 2025-12-09 2026-01-08 2025-12-09 02:19:03.535855+00 [{"amount": "139.00", "quantity": 1, "unit_price": "139.00", "description": "Growth Plan - Dec 2025"}] \N \N {"subscription_id": 9, "billing_snapshot": {"city": "", "email": "paid2@paid.com", "state": "", "tax_id": "", "country": "", "postal_code": "", "address_line1": "", "address_line2": "", "snapshot_date": "2025-12-09T02:03:03.127165+00:00"}, "billing_period_end": "2026-01-08T02:03:02.875465+00:00", "billing_period_start": "2025-12-09T02:03:02.875465+00:00"} 2025-12-09 02:03:03.131286+00 2025-12-09 02:03:03.132833+00 90 USD \N
+\.
+
+
+--
+-- Data for Name: igny8_keywords; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_keywords (id, volume_override, difficulty_override, attribute_values, status, created_at, updated_at, tenant_id, cluster_id, sector_id, seed_keyword_id, site_id, disabled, delete_reason, deleted_at, deleted_by_id, is_deleted, restore_until) FROM stdin;
+793 \N \N [] mapped 2025-12-28 00:49:25.123707+00 2025-12-28 00:49:25.123718+00 90 425 61 154 21 f \N \N \N f \N
+794 \N \N [] mapped 2025-12-28 00:49:25.12762+00 2025-12-28 00:49:25.127633+00 90 425 61 167 21 f \N \N \N f \N
+795 \N \N [] mapped 2025-12-28 00:49:25.130716+00 2025-12-28 00:49:25.130723+00 90 426 61 159 21 f \N \N \N f \N
+797 \N \N [] mapped 2025-12-28 02:21:45.131678+00 2025-12-28 02:21:45.131684+00 90 424 61 180 21 f \N \N \N f \N
+606 \N \N [] mapped 2025-12-15 13:33:13.511085+00 2025-12-15 13:33:13.511091+00 5 \N 19 6 5 f api_delete 2026-01-13 00:24:58.712804+00 119 t 2026-01-27 00:24:58.712804+00
+543 \N \N [] mapped 2025-12-10 08:44:40.262264+00 2025-12-10 08:44:40.262279+00 5 \N 15 47 5 f api_delete 2026-01-13 00:24:58.747564+00 119 t 2026-01-27 00:24:58.747564+00
+506 \N \N [] mapped 2025-12-05 12:00:58.288837+00 2025-12-05 12:00:58.288847+00 5 \N 17 25 5 f api_delete 2026-01-13 00:24:58.761895+00 119 t 2026-01-27 00:24:58.761895+00
+500 \N \N [] mapped 2025-12-05 12:00:58.266115+00 2025-12-05 12:00:58.266131+00 5 \N 17 21 5 f api_delete 2026-01-13 00:24:58.774125+00 119 t 2026-01-27 00:24:58.774125+00
+492 \N \N [] mapped 2025-12-04 15:42:08.922722+00 2025-12-04 15:42:08.922736+00 5 \N 16 40 5 f api_delete 2026-01-13 00:24:58.789859+00 119 t 2026-01-27 00:24:58.789859+00
+485 \N \N [] mapped 2025-12-04 10:51:33.928033+00 2025-12-04 10:51:33.928044+00 5 \N 18 11 5 f api_delete 2026-01-13 00:24:58.809701+00 119 t 2026-01-27 00:24:58.809701+00
+796 \N \N [] mapped 2025-12-28 02:21:45.126787+00 2025-12-28 02:21:45.126802+00 90 426 61 157 21 f \N \N \N f \N
+798 \N \N [] mapped 2025-12-28 02:21:45.134384+00 2025-12-28 02:21:45.13439+00 90 426 61 160 21 f \N \N \N f \N
+578 \N \N [] new 2025-12-12 21:30:38.553071+00 2025-12-12 21:30:38.553077+00 5 \N 15 49 5 f api_delete 2026-01-13 00:24:58.738177+00 119 t 2026-01-27 00:24:58.738177+00
+577 \N \N [] new 2025-12-12 21:30:38.549775+00 2025-12-12 21:30:38.549782+00 5 \N 15 41 5 f api_delete 2026-01-13 00:24:58.739996+00 119 t 2026-01-27 00:24:58.739996+00
+576 \N \N [] new 2025-12-12 21:30:38.546391+00 2025-12-12 21:30:38.546401+00 5 \N 15 46 5 f api_delete 2026-01-13 00:24:58.741743+00 119 t 2026-01-27 00:24:58.741743+00
+540 \N \N [] new 2025-12-10 08:44:28.33967+00 2025-12-10 08:44:28.339682+00 5 \N 15 48 5 f api_delete 2026-01-13 00:24:58.753759+00 119 t 2026-01-27 00:24:58.753759+00
+539 \N \N [] new 2025-12-10 08:44:28.3338+00 2025-12-10 08:44:28.33382+00 5 \N 15 45 5 f api_delete 2026-01-13 00:24:58.755659+00 119 t 2026-01-27 00:24:58.755659+00
+799 \N \N [] mapped 2025-12-29 02:32:59.410863+00 2025-12-29 02:32:59.410875+00 90 424 61 146 21 f \N \N \N f \N
+800 \N \N [] mapped 2025-12-29 02:32:59.423157+00 2025-12-29 02:32:59.42317+00 90 424 61 181 21 f \N \N \N f \N
+801 \N \N [] mapped 2025-12-29 02:32:59.426821+00 2025-12-29 02:32:59.42683+00 90 424 61 178 21 f \N \N \N f \N
+802 \N \N [] mapped 2025-12-29 02:32:59.429928+00 2025-12-29 02:32:59.429934+00 90 424 61 140 21 f \N \N \N f \N
+803 \N \N [] mapped 2025-12-29 02:32:59.433696+00 2025-12-29 02:32:59.433703+00 90 424 61 142 21 f \N \N \N f \N
+804 \N \N [] mapped 2025-12-29 07:23:54.524787+00 2025-12-29 07:23:54.524798+00 90 425 61 150 21 f \N \N \N f \N
+805 \N \N [] mapped 2026-01-05 01:20:25.9458+00 2026-01-05 01:20:25.945814+00 90 427 61 172 21 f \N \N \N f \N
+806 \N \N [] mapped 2026-01-05 01:20:25.968051+00 2026-01-05 01:20:25.968062+00 90 427 61 188 21 f \N \N \N f \N
+807 \N \N [] mapped 2026-01-05 01:20:25.972071+00 2026-01-05 01:20:25.97208+00 90 427 61 176 21 f \N \N \N f \N
+808 \N \N [] mapped 2026-01-05 01:20:25.976185+00 2026-01-05 01:20:25.976197+00 90 427 61 141 21 f \N \N \N f \N
+809 \N \N [] mapped 2026-01-05 01:20:25.980746+00 2026-01-05 01:20:25.980761+00 90 427 61 153 21 f \N \N \N f \N
+602 \N \N [] new 2025-12-15 13:32:51.022442+00 2025-12-15 13:32:51.02245+00 5 \N 16 33 5 f api_delete 2026-01-13 00:24:58.720825+00 119 t 2026-01-27 00:24:58.720825+00
+601 \N \N [] new 2025-12-15 13:32:51.019008+00 2025-12-15 13:32:51.019016+00 5 \N 16 35 5 f api_delete 2026-01-13 00:24:58.722865+00 119 t 2026-01-27 00:24:58.722865+00
+600 \N \N [] new 2025-12-15 13:32:51.015872+00 2025-12-15 13:32:51.015887+00 5 \N 16 39 5 f api_delete 2026-01-13 00:24:58.724842+00 119 t 2026-01-27 00:24:58.724842+00
+599 \N \N [] new 2025-12-15 13:32:51.009477+00 2025-12-15 13:32:51.009493+00 5 \N 16 37 5 f api_delete 2026-01-13 00:24:58.72672+00 119 t 2026-01-27 00:24:58.72672+00
+505 \N \N [] mapped 2025-12-05 12:00:58.284833+00 2025-12-05 12:00:58.284844+00 5 \N 17 22 5 f api_delete 2026-01-13 00:24:58.763881+00 119 t 2026-01-27 00:24:58.763881+00
+496 \N \N [] mapped 2025-12-04 15:49:58.23829+00 2025-12-04 15:49:58.2383+00 5 \N 16 38 5 f api_delete 2026-01-13 00:24:58.778604+00 119 t 2026-01-27 00:24:58.778604+00
+488 \N \N [] mapped 2025-12-04 10:57:25.509187+00 2025-12-04 10:57:25.509203+00 5 \N 18 12 5 f api_delete 2026-01-13 00:24:58.80126+00 119 t 2026-01-27 00:24:58.80126+00
+487 \N \N [] mapped 2025-12-04 10:57:25.503535+00 2025-12-04 10:57:25.503552+00 5 \N 18 20 5 f api_delete 2026-01-13 00:24:58.804566+00 119 t 2026-01-27 00:24:58.804566+00
+484 \N \N [] mapped 2025-12-04 10:50:51.902866+00 2025-12-04 10:50:51.902876+00 5 \N 18 19 5 f api_delete 2026-01-13 00:24:58.812649+00 119 t 2026-01-27 00:24:58.812649+00
+483 \N \N [] mapped 2025-12-04 10:50:51.900076+00 2025-12-04 10:50:51.90009+00 5 \N 18 18 5 f api_delete 2026-01-13 00:24:58.815126+00 119 t 2026-01-27 00:24:58.815126+00
+607 \N \N [] mapped 2025-12-15 13:33:13.51424+00 2025-12-15 13:33:13.514246+00 5 \N 19 9 5 f api_delete 2026-01-13 00:24:58.70471+00 119 t 2026-01-27 00:24:58.70471+00
+605 \N \N [] mapped 2025-12-15 13:33:13.508267+00 2025-12-15 13:33:13.508274+00 5 \N 19 1 5 f api_delete 2026-01-13 00:24:58.714887+00 119 t 2026-01-27 00:24:58.714887+00
+604 \N \N [] mapped 2025-12-15 13:33:13.505834+00 2025-12-15 13:33:13.505843+00 5 \N 19 3 5 f api_delete 2026-01-13 00:24:58.716952+00 119 t 2026-01-27 00:24:58.716952+00
+603 \N \N [] mapped 2025-12-15 13:33:13.502377+00 2025-12-15 13:33:13.502394+00 5 \N 19 7 5 f api_delete 2026-01-13 00:24:58.718762+00 119 t 2026-01-27 00:24:58.718762+00
+545 \N \N [] mapped 2025-12-10 08:44:40.27399+00 2025-12-10 08:44:40.274003+00 5 \N 15 50 5 f api_delete 2026-01-13 00:24:58.743556+00 119 t 2026-01-27 00:24:58.743556+00
+542 \N \N [] mapped 2025-12-10 08:44:28.346697+00 2025-12-10 08:44:28.346707+00 5 \N 15 44 5 f api_delete 2026-01-13 00:24:58.749625+00 119 t 2026-01-27 00:24:58.749625+00
+541 \N \N [] mapped 2025-12-10 08:44:28.343179+00 2025-12-10 08:44:28.343186+00 5 \N 15 42 5 f api_delete 2026-01-13 00:24:58.75183+00 119 t 2026-01-27 00:24:58.75183+00
+508 \N \N [] mapped 2025-12-05 12:00:58.299587+00 2025-12-05 12:00:58.299598+00 5 \N 17 23 5 f api_delete 2026-01-13 00:24:58.757613+00 119 t 2026-01-27 00:24:58.757613+00
+507 \N \N [] mapped 2025-12-05 12:00:58.293592+00 2025-12-05 12:00:58.293602+00 5 \N 17 28 5 f api_delete 2026-01-13 00:24:58.759855+00 119 t 2026-01-27 00:24:58.759855+00
+504 \N \N [] mapped 2025-12-05 12:00:58.281413+00 2025-12-05 12:00:58.281429+00 5 \N 17 24 5 f api_delete 2026-01-13 00:24:58.765806+00 119 t 2026-01-27 00:24:58.765806+00
+503 \N \N [] mapped 2025-12-05 12:00:58.276406+00 2025-12-05 12:00:58.276414+00 5 \N 17 29 5 f api_delete 2026-01-13 00:24:58.767729+00 119 t 2026-01-27 00:24:58.767729+00
+502 \N \N [] mapped 2025-12-05 12:00:58.273607+00 2025-12-05 12:00:58.273618+00 5 \N 17 30 5 f api_delete 2026-01-13 00:24:58.769727+00 119 t 2026-01-27 00:24:58.769727+00
+501 \N \N [] mapped 2025-12-05 12:00:58.270279+00 2025-12-05 12:00:58.270295+00 5 \N 17 26 5 f api_delete 2026-01-13 00:24:58.771797+00 119 t 2026-01-27 00:24:58.771797+00
+499 \N \N [] mapped 2025-12-05 12:00:58.252464+00 2025-12-05 12:00:58.25248+00 5 \N 17 27 5 f api_delete 2026-01-13 00:24:58.776187+00 119 t 2026-01-27 00:24:58.776187+00
+495 \N \N [] mapped 2025-12-04 15:49:58.235815+00 2025-12-04 15:49:58.235825+00 5 \N 16 31 5 f api_delete 2026-01-13 00:24:58.78132+00 119 t 2026-01-27 00:24:58.78132+00
+494 \N \N [] mapped 2025-12-04 15:49:58.231961+00 2025-12-04 15:49:58.231976+00 5 \N 16 36 5 f api_delete 2026-01-13 00:24:58.784631+00 119 t 2026-01-27 00:24:58.784631+00
+493 \N \N [] mapped 2025-12-04 15:42:08.926186+00 2025-12-04 15:42:08.926197+00 5 \N 16 34 5 f api_delete 2026-01-13 00:24:58.787393+00 119 t 2026-01-27 00:24:58.787393+00
+491 \N \N [] mapped 2025-12-04 15:42:08.914587+00 2025-12-04 15:42:08.914607+00 5 \N 16 32 5 f api_delete 2026-01-13 00:24:58.792483+00 119 t 2026-01-27 00:24:58.792483+00
+490 \N \N [] mapped 2025-12-04 11:06:17.402763+00 2025-12-04 11:06:17.402772+00 5 \N 18 16 5 f api_delete 2026-01-13 00:24:58.794715+00 119 t 2026-01-27 00:24:58.794715+00
+489 \N \N [] mapped 2025-12-04 11:06:17.399715+00 2025-12-04 11:06:17.399732+00 5 \N 18 13 5 f api_delete 2026-01-13 00:24:58.797219+00 119 t 2026-01-27 00:24:58.797219+00
+486 \N \N [] mapped 2025-12-04 10:51:33.931106+00 2025-12-04 10:51:33.931113+00 5 \N 18 15 5 f api_delete 2026-01-13 00:24:58.807036+00 119 t 2026-01-27 00:24:58.807036+00
+482 \N \N [] mapped 2025-12-04 10:49:56.901996+00 2025-12-04 10:49:56.902005+00 5 \N 18 14 5 f api_delete 2026-01-13 00:24:58.818031+00 119 t 2026-01-27 00:24:58.818031+00
+481 \N \N [] mapped 2025-12-04 10:49:56.898876+00 2025-12-04 10:49:56.89889+00 5 \N 18 17 5 f api_delete 2026-01-13 00:24:58.820081+00 119 t 2026-01-27 00:24:58.820081+00
+598 \N \N [] mapped 2025-12-14 19:36:53.745768+00 2025-12-14 19:36:53.745783+00 5 \N 19 5 5 f api_delete 2026-01-13 00:24:58.728726+00 119 t 2026-01-27 00:24:58.728726+00
+597 \N \N [] mapped 2025-12-14 19:36:53.74195+00 2025-12-14 19:36:53.741957+00 5 \N 19 10 5 f api_delete 2026-01-13 00:24:58.730723+00 119 t 2026-01-27 00:24:58.730723+00
+596 \N \N [] mapped 2025-12-14 19:36:53.738995+00 2025-12-14 19:36:53.739001+00 5 \N 19 2 5 f api_delete 2026-01-13 00:24:58.732478+00 119 t 2026-01-27 00:24:58.732478+00
+595 \N \N [] mapped 2025-12-14 19:36:53.7355+00 2025-12-14 19:36:53.735513+00 5 \N 19 4 5 f api_delete 2026-01-13 00:24:58.734358+00 119 t 2026-01-27 00:24:58.734358+00
+594 \N \N [] mapped 2025-12-14 19:36:53.722862+00 2025-12-14 19:36:53.722871+00 5 \N 19 8 5 f api_delete 2026-01-13 00:24:58.736162+00 119 t 2026-01-27 00:24:58.736162+00
+544 \N \N [] mapped 2025-12-10 08:44:40.26787+00 2025-12-10 08:44:40.267886+00 5 \N 15 43 5 f api_delete 2026-01-13 00:24:58.745492+00 119 t 2026-01-27 00:24:58.745492+00
+\.
+
+
+--
+-- Data for Name: igny8_module_enable_settings; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_module_enable_settings (id, created_at, updated_at, planner_enabled, writer_enabled, thinker_enabled, automation_enabled, site_builder_enabled, linker_enabled, optimizer_enabled, publisher_enabled, tenant_id) FROM stdin;
+5 2025-12-10 07:54:17.117103+00 2025-12-10 07:54:17.117118+00 t t t t f f f t 90
+1 2025-11-16 21:14:36.378469+00 2025-12-13 21:14:56.002458+00 t t t t f f f f 5
+\.
+
+
+--
+-- Data for Name: igny8_module_settings; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_module_settings (id, is_active, updated_at, created_at, module_name, key, tenant_id) FROM stdin;
+\.
+
+
+--
+-- Data for Name: igny8_optimization_tasks; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_optimization_tasks (id, scores_before, scores_after, html_before, html_after, status, credits_used, metadata, created_at, updated_at, tenant_id, content_id) FROM stdin;
+\.
+
+
+--
+-- Data for Name: igny8_page_blueprints; Type: TABLE DATA; Schema: public; Owner: igny8
+--
+
+COPY public.igny8_page_blueprints (id, slug, title, type, blocks_json, status, "order", created_at, updated_at, tenant_id, sector_id, site_id, site_blueprint_id) FROM stdin;
+68 services Services services [{"type": "services", "layout": "two-column", "content": ["Home design consultations to optimize your layout.", "Custom storage solutions designed for functionality and style.", "Online workshops to inspire your DIY projects."], "heading": "Our Expert Services", "subheading": "Tailored solutions for every space."}, {"type": "stats", "layout": "full-width", "content": ["Over 1,000 satisfied customers served.", "98% customer satisfaction rate.", "100+ successful projects completed."], "heading": "Our Impact", "subheading": "Results that speak for themselves."}, {"type": "faq", "layout": "two-column", "content": ["What is your consultation process?", "How do you ensure sustainability in your products?", "Can I see a portfolio of past projects?"], "heading": "Frequently Asked Questions", "subheading": "Your questions answered."}] draft 1 2025-11-20 22:44:37.818351+00 2025-11-20 22:44:37.818357+00 5 15 5 16
+69 about About Us about [{"type": "hero", "layout": "full-width", "content": ["An engaging image reflecting the journey of the brand."], "heading": "Our Story", "subheading": "Building beautiful homes, one space at a time."}, {"type": "features", "layout": "two-column", "content": ["Commitment to quality and craftsmanship.", "Passion for sustainable living.", "Dedication to customer satisfaction."], "heading": "Our Values", "subheading": "What drives us."}, {"type": "team", "layout": "cards", "content": ["Brief bios of key team members with images.", "Highlight their expertise and role in the company."], "heading": "Meet the Team", "subheading": "The faces behind Home & Garden."}] draft 2 2025-11-20 22:44:37.820437+00 2025-11-20 22:44:37.820443+00 5 15 5 16
+70 blog Blog blog [{"type": "hero", "layout": "full-width", "content": ["Featured image representing the essence of home decor."], "heading": "Latest Insights", "subheading": "Stay updated with the latest trends in home decor."}, {"type": "features", "layout": "cards", "content": ["Article summary with links to full posts.", "Include images to capture interest."], "heading": "Popular Articles", "subheading": "Our most read articles."}, {"type": "newsletter", "layout": "full-width", "content": ["Email subscription form to capture leads."], "heading": "Join Our Community", "subheading": "Get the latest tips directly to your inbox."}] draft 3 2025-11-20 22:44:37.822384+00 2025-11-20 22:44:37.82239+00 5 15 5 16
+71 contact Contact Us contact [{"type": "hero", "layout": "full-width", "content": ["An inviting image of customer service interaction."], "heading": "We're Here to Help", "subheading": "Reach out with any questions or requests."}, {"type": "contact", "layout": "two-column", "content": ["Contact form for inquiries.", "Display of phone number and email address."], "heading": "Get in Touch", "subheading": "We'd love to hear from you."}, {"type": "faq", "layout": "two-column", "content": ["How long does a project typically take?", "What is your pricing structure?", "What areas do you serve?"], "heading": "Common Questions", "subheading": "Find answers to common queries."}] draft 4 2025-11-20 22:44:37.824378+00 2025-11-20 22:44:37.824384+00 5 15 5 16
+72 faq FAQ custom [{"type": "hero", "layout": "full-width", "content": ["An engaging image that represents support and assistance."], "heading": "Frequently Asked Questions", "subheading": "Your queries answered."}, {"type": "faq", "layout": "two-column", "content": ["What are your operating hours?", "Do you offer online consultations?", "What payment methods do you accept?"], "heading": "General Questions", "subheading": "We’ve got you covered."}, {"type": "faq", "layout": "two-column", "content": ["Can I customize my order?", "What is your return policy?", "Do you provide installation services?"], "heading": "Service-Specific Questions", "subheading": "Learn more about our offerings."}] draft 5 2025-11-20 22:44:37.826491+00 2025-11-20 22:44:37.826497+00 5 15 5 16
+67 home Home home [{"data": {"content": "At Home & Garden Site, we offer a carefully curated selection of products that elevate your home and garden experience. Discover stylish decor, essential tools, and more.", "heading": "Transform Your Home & Garden with Our Quality Products", "buttonLink": "/shop", "buttonText": "Shop Now", "subheading": "Everything you need to create your perfect space"}, "type": "hero"}, {"data": {"content": " [Heading]
\r\n[Subheading]
\r\n[Subheading]
\r\n[Subheading]
\r\n\r\n
\r\n\r\n\r\n[Subheading]
\r\n\r\n\r\n
\r\n\r\n \r\n\r\n\r\n Col1 Col2 Col3 Col4 \r\n \r\n\r\nData Data Data Data `, `
`, `
`, `
`, `
`, ``, ``, `
`, ` `, ` `\r\n\r\n---\r\n\r\n## WRITING RULES\r\n\r\n### DO:\r\n✓ Use specific examples, brands, models, numbers\r\n✓ Explain mechanisms and technical details\r\n✓ Include real data (prices, percentages, measurements)\r\n✓ Write naturally with varied sentence length\r\n✓ Use active voice\r\n✓ Connect ideas logically between paragraphs\r\n\r\n### DON'T:\r\n✗ Generic openings (\"In today's world...\")\r\n✗ Repeat H2/H3 in first sentence\r\n✗ Robotic transitions (\"First...\", \"Second...\")\r\n✗ Filler phrases (\"It's important to note...\")\r\n✗ Placeholder content (\"Brand A\", \"Model X\", \"Data 1\")\r\n✗ Paragraphs under 40 words or over 80 words\r\n✗ Lists with more than 6 items or items over 25 words\r\n✗ Tables with more than 5 columns or 6 rows\r\n✗ Writing more than 1200 words total\r\n\r\n---\r\n\r\n## KEYWORD USAGE\r\n\r\n**Primary keyword** (identify from title):\r\n- Use in title, intro, meta title/description\r\n- Include in 2-3 H2 headings naturally\r\n- Mention 2-3 times in content (0.5-1% density)\r\n\r\n**Secondary keywords** (3-4 from keyword list):\r\n- Distribute across H2 sections\r\n- Use in H2/H3 headings where natural\r\n- 2-3 mentions each (0.3-0.6% density)\r\n- Include variations and related terms\r\n\r\n---\r\n\r\n## METADATA\r\n\r\n**Meta Title:** Under 60 chars, primary keyword included, action-oriented\r\n**Meta Description:** 140-160 chars, primary keyword, clear value proposition\r\n**Tags:** 5 tags, 2-4 words each, lowercase, topically relevant\r\n**Categories:** 1-2 in format \"Parent > Child\"\r\n\r\n---\r\n\r\n## VERIFICATION BEFORE OUTPUT\r\n\r\n- [ ] 1000-1200 words ONLY (excluding HTML tags) - STOP if exceeding\r\n- [ ] 6 H2 sections\r\n- [ ] Maximum 2 sections with lists\r\n- [ ] Maximum 2 sections with tables\r\n- [ ] Random sections sequence with differnet format\r\n- [ ] All paragraphs 50-80 words\r\n- [ ] All lists 4-5 items, 15-20 words each\r\n- [ ] All tables 4-5 columns, 5-6 rows, real data\r\n- [ ] No placeholder content anywhere\r\n- [ ] Primary keyword optimized correctly\r\n- [ ] Meta title <60 chars, description <160 chars\r\n- [ ] Valid JSON with escaped quotes\r\n\r\n---\r\n\r\n## IMPORTANT\r\n\r\nReturn ONLY valid JSON. No explanatory text. Ensure word_count reflects actual content words.",
+ "description": "",
+ "variables": [],
+ "is_active": true,
+ "version": 1,
+ "last_updated": "2025-12-20T20:21:47.267Z",
+ "created_at": "2025-12-20T12:53:51.906Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 2,
+ "fields": {
+ "prompt_type": "ideas",
+ "prompt_value": "# SEO Content Idea Generator\r\n\r\nYou are a content strategist. Generate content ideas and simple outlines for keyword clusters. The actual content will be written by a separate system.\r\n\r\n---\r\n\r\n## INPUT FORMAT\r\n\r\n**Clusters to analyze:**\r\n[IGNY8_CLUSTERS]\r\n\r\n**Keywords in each cluster:**\r\n[IGNY8_CLUSTER_KEYWORDS]\r\n\r\n---\r\n\r\n## OUTPUT REQUIREMENTS\r\n\r\nGenerate exactly **4 content ideas per cluster**:\r\n- 1 cluster hub page (comprehensive overview)\r\n- 3 supporting articles (focused on specific angles)\r\n\r\n---\r\n\r\n## OUTPUT JSON OBJECT STRUCTURE\r\n\r\n\r\n## OUTPUT JSON OBJECT STRUCTURE\r\n{\r\n \"ideas\": [\r\n {\r\n \"title\": \"[Compelling title with primary keyword]\",\r\n \"description\": {\r\n \"overview\": \"[2-3 sentence description of what this content covers and its unique angle]\",\r\n \"outline\": {\r\n \"intro_focus\": \"[What the introduction should establish]\",\r\n \"main_sections\": [\r\n {\r\n \"h2_topic\": \"[Section topic/angle]\",\r\n \"coverage\": \"[What this section should cover - 1 sentence]\"\r\n }\r\n ]\r\n }\r\n },\r\n \"covered_keywords\": \"[3-8 relevant keywords from cluster, comma-separated]\",\r\n \"content_type\": \"post|page\",\r\n \"content_structure\": \"cluster_hub|guide_tutorial|how_to|comparison|review|top_listicle|question\",\r\n \"cluster_id\": \"[Cluster ID number]\",\r\n \"estimated_word_count\": 1500\r\n }\r\n ]\r\n}\r\n\r\n---\r\n\r\n## CONTENT STRUCTURE BY TYPE\r\n\r\n### Cluster Hub (1 per cluster)\r\n- **Purpose**: Comprehensive overview of entire topic cluster\r\n- **Sections**: 8-10 H2 sections\r\n- **Coverage**: Introduces all major subtopics, links to supporting content\r\n- **Keywords**: Covers 5-8 keywords from cluster naturally\r\n\r\n### Guide/Tutorial (Supporting)\r\n- **Purpose**: Step-by-step educational content\r\n- **Sections**: 6-8 H2 sections\r\n- **Coverage**: Process-oriented, actionable steps\r\n- **Keywords**: Focuses on 2-4 specific keywords\r\n\r\n### How-To (Supporting)\r\n- **Purpose**: Solve a specific problem\r\n- **Sections**: 5-7 H2 sections\r\n- **Coverage**: Problem → solution framework\r\n- **Keywords**: Long-tail, question-based keywords\r\n\r\n### Comparison (Supporting)\r\n- **Purpose**: Compare options/alternatives\r\n- **Sections**: 6-8 H2 sections\r\n- **Coverage**: Feature analysis, pros/cons, recommendations\r\n- **Keywords**: \"vs\", \"best\", \"alternative\" keywords\r\n\r\n### Review (Supporting)\r\n- **Purpose**: Evaluate specific products/services\r\n- **Sections**: 6-8 H2 sections\r\n- **Coverage**: Features, testing, verdict\r\n- **Keywords**: Product/brand names + descriptive terms\r\n\r\n### Top Listicle (Supporting)\r\n- **Purpose**: Curated ranked list\r\n- **Sections**: 6-8 H2 sections (intro + items + conclusion)\r\n- **Coverage**: Criteria, ranked items, selection guide\r\n- **Keywords**: \"best\", \"top\", number-based keywords\r\n\r\n### Question (Supporting)\r\n- **Purpose**: Answer specific query\r\n- **Sections**: 5-7 H2 sections\r\n- **Coverage**: Question → context → answer → implications\r\n- **Keywords**: Question keywords, related queries\r\n\r\n---\r\n\r\n## OUTLINE REQUIREMENTS\r\n\r\nFor each idea, provide:\r\n\r\n1. **Intro Focus**: What angle/hook the introduction should take (1 sentence)\r\n\r\n2. **Main Sections**: 5-10 H2 topics with brief coverage notes\r\n - List the H2 section topics only\r\n - 1 sentence on what each section should cover\r\n - No need for H3 breakdown (content generator will handle)\r\n - No formatting details (content generator will handle)\r\n\r\n3. **Section suggestions**:\r\n - Foundation/basics sections (if needed)\r\n - Core concept sections (main body)\r\n - Application/implementation sections\r\n - Advanced/future sections (if appropriate)\r\n\r\n---\r\n\r\n## TITLE GUIDELINES\r\n\r\n- Naturally include primary keyword\r\n- 50-65 characters ideal\r\n- Compelling and specific\r\n- Match content structure type:\r\n - Hub: \"Complete Guide to [Topic]\"\r\n - How-to: \"How to [Action] [Object/Goal]\"\r\n - Comparison: \"[X] vs [Y]: Which Is Better?\"\r\n - Review: \"[Product/Service] Review: [Key Benefit]\"\r\n - Listicle: \"[Number] Best [Items] for [Purpose]\"\r\n - Question: \"[Question Using Primary Keyword]?\"\r\n\r\n---\r\n\r\n## KEYWORD COVERAGE\r\n\r\n### Covered Keywords (5-8 per idea)\r\nList all relevant keywords from the cluster that this content will naturally address:\r\n- Include the main keyword that appears in the title\r\n- Include related search terms from cluster\r\n- Include natural variations and long-tail keywords\r\n- Include supporting/related terms\r\n\r\n**The content generator will automatically:**\r\n- Identify the primary keyword from title and context\r\n- Categorize remaining keywords as secondary\r\n- Optimize keyword placement and density\r\n\r\n### Coverage Strategy\r\n- **Cluster Hub**: 6-10 keywords (comprehensive coverage)\r\n- **Supporting Content**: 4-6 keywords (focused coverage)\r\n\r\n---\r\n\r\n## CONTENT ANGLE REQUIREMENTS\r\n\r\nEach idea must have a **unique angle**:\r\n\r\n✓ Different content structure types across the 4 ideas\r\n✓ Different target intents (informational, commercial, navigational)\r\n✓ Different depth levels (overview vs deep-dive)\r\n✓ No duplicate section topics across ideas in same cluster\r\n\r\n**Example for \"Email Marketing\" cluster:**\r\n1. Hub: \"Complete Guide to Email Marketing\" \r\n - covered_keywords: \"email marketing, email campaigns, newsletter marketing, email automation, email list building, email marketing strategy\"\r\n2. Supporting: \"How to Build an Email List from Scratch\"\r\n - covered_keywords: \"email list building, grow email list, subscriber acquisition, lead magnets, email signup forms\"\r\n3. Supporting: \"Mailchimp vs ConvertKit: Email Platform Comparison\"\r\n - covered_keywords: \"mailchimp vs convertkit, email marketing platforms, email software comparison, best email tools\"\r\n4. Supporting: \"10 Best Email Marketing Tools for Small Businesses\"\r\n - covered_keywords: \"email marketing tools, email software, email marketing platforms, small business email marketing\"\r\n\r\n---\r\n\r\n## QUALITY CHECKS\r\n\r\nBefore finalizing, verify:\r\n- ✓ 4 ideas per cluster (1 hub + 3 supporting)\r\n- ✓ Each idea has unique content_structure type\r\n- ✓ Title includes a relevant keyword from cluster\r\n- ✓ 5-10 H2 sections outlined per idea\r\n- ✓ 5-8 covered_keywords listed per idea\r\n- ✓ Content angles don't overlap\r\n- ✓ Valid JSON format\r\n\r\n---\r\n\r\n## OUTPUT FORMAT\r\n\r\nReturn ONLY valid JSON with no comments or explanations.\r\n\r\nThe \"outline\" object should be simple and high-level - just enough to guide the content generator. The actual detailed structure, word counts, formatting, H3 subsections, lists, tables, etc. will be handled by the content generation system.\r\n\r\n---\r\n\r\n## WHAT NOT TO INCLUDE\r\n\r\n❌ Detailed H3 subsections (content generator handles this)\r\n❌ Specific formatting instructions (paragraph/list/table details)\r\n❌ Word count per section (content generator calculates)\r\n❌ Detailed content descriptions (keep coverage notes brief)\r\n❌ HTML structure (content generator outputs HTML)\r\n❌ Introduction hook text (content generator writes this)\r\n\r\nKeep outlines strategic and high-level. Let the content generation system handle tactical execution.",
+ "description": "",
+ "variables": {
+ "ideas": [
+ {
+ "title": "[Compelling title with primary keyword]",
+ "cluster_id": "[Cluster ID number]",
+ "description": {
+ "outline": {
+ "intro_focus": "[What the introduction should establish]",
+ "main_sections": [
+ {
+ "coverage": "[What this section should cover - 1 sentence]",
+ "h2_topic": "[Section topic/angle]"
+ }
+ ]
+ },
+ "overview": "[2-3 sentence description of what this content covers and its unique angle]"
+ },
+ "content_type": "post|page",
+ "covered_keywords": "[3-8 relevant keywords from cluster, comma-separated]",
+ "content_structure": "cluster_hub|guide_tutorial|how_to|comparison|review|top_listicle|question",
+ "estimated_word_count": 1500
+ }
+ ]
+ },
+ "is_active": true,
+ "version": 1,
+ "last_updated": "2025-12-20T19:27:56.816Z",
+ "created_at": "2025-12-20T12:53:51.905Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 4,
+ "fields": {
+ "prompt_type": "image_prompt_extraction",
+ "prompt_value": "Extract image prompts from the following article content.\r\n\r\n**ARTICLE TITLE:** {title}\r\n\r\n**ARTICLE CONTENT:** {content}\r\n\r\n**INSTRUCTIONS:**\r\n\r\nExtract image prompts for:\r\n1. **Featured Image:** One main image that represents the article topic\r\n2. **In-Article Images:** Up to {max_images} images that would be useful within the article content\r\n3. **Content Paragraphs (caption):** For each image, write a natural content paragraph (40-60 words) that discusses the topic represented by the image. This should read as regular article content that flows naturally with the surrounding text, NOT as an image description or caption. The paragraph should provide substantive information about the topic while contextually relating to what the image shows.\r\n\r\n**Return a JSON object with this structure:**\r\n\r\n{{\r\n \"featured_prompt\": \"Detailed description of the featured image (max 600 characters)\",\r\n \"featured_caption\": \"A 40-60 word paragraph that serves as both an image caption and represents content from the article, providing context naturally\",\r\n \"in_article_prompts\": [\r\n {{\r\n \"prompt\": \"Description of first in-article image (max 600 characters)\",\r\n \"caption\": \"A 40-60 word paragraph describing this image while incorporating relevant content from the corresponding section\"\r\n }},\r\n {{\r\n \"prompt\": \"Description of second in-article image (max 600 characters)\",\r\n \"caption\": \"A 40-60 word paragraph describing this image while incorporating relevant content from the corresponding section\"\r\n }}\r\n ]\r\n}}\r\n\r\n**Requirements:**\r\n- Each prompt must be detailed enough for image generation, describing visual elements, style, mood, and composition\r\n- Maximum prompt length: 600 characters per image prompt\r\n- Caption length: 40-60 words each\r\n- Captions should blend image description with article content naturally",
+ "description": "",
+ "variables": [],
+ "is_active": true,
+ "version": 1,
+ "last_updated": "2025-12-25T02:05:23.641Z",
+ "created_at": "2025-12-20T12:53:51.908Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 5,
+ "fields": {
+ "prompt_type": "image_prompt_template",
+ "prompt_value": "Image Type: {image_type},\r\nImage Prompt: {image_prompt}",
+ "description": "",
+ "variables": [],
+ "is_active": true,
+ "version": 1,
+ "last_updated": "2025-12-20T19:57:17.579Z",
+ "created_at": "2025-12-20T12:53:51.910Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 6,
+ "fields": {
+ "prompt_type": "negative_prompt",
+ "prompt_value": "text, watermark, logo, signature, username, artist name, blurry, low quality, pixelated, distorted, deformed, duplicate, cropped, out of frame, bad anatomy, bad proportions, extra limbs, missing limbs, floating limbs, disconnected limbs, mutation, mutated, ugly, disgusting, amputation, cartoon, anime",
+ "description": "",
+ "variables": [],
+ "is_active": true,
+ "version": 1,
+ "last_updated": "2025-12-20T19:57:43.034Z",
+ "created_at": "2025-12-20T12:53:51.912Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 8,
+ "fields": {
+ "prompt_type": "product_generation",
+ "prompt_value": "Create comprehensive product content for:\r\n\r\nProduct Name: {product_name}\r\nCategory: {category}\r\nFeatures: {features}\r\nTarget Audience: {audience}\r\n\r\nGenerate:\r\n1. Compelling product description (200-300 words)\r\n2. Key features and benefits (bullet points)\r\n3. Technical specifications\r\n4. Use cases or applications\r\n5. SEO-optimized meta description\r\n\r\nReturn structured JSON with all elements.",
+ "description": "",
+ "variables": [],
+ "is_active": false,
+ "version": 1,
+ "last_updated": "2025-12-20T19:58:14.244Z",
+ "created_at": "2025-12-20T12:53:51.917Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 9,
+ "fields": {
+ "prompt_type": "service_generation",
+ "prompt_value": "Create detailed service page content for:\r\n\r\nService Name: {service_name}\r\nCategory: {category}\r\nKey Benefits: {benefits}\r\nTarget Audience: {audience}\r\n\r\nGenerate:\r\n1. Overview section (150-200 words)\r\n2. Process or methodology (step-by-step)\r\n3. Benefits and outcomes\r\n4. Why choose us / differentiators\r\n5. FAQ section (5-7 questions)\r\n6. Call-to-action suggestions\r\n\r\nReturn structured HTML content.",
+ "description": "",
+ "variables": [],
+ "is_active": false,
+ "version": 1,
+ "last_updated": "2025-12-20T19:58:23.805Z",
+ "created_at": "2025-12-20T12:53:51.919Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 7,
+ "fields": {
+ "prompt_type": "site_structure_generation",
+ "prompt_value": "Design a comprehensive site structure for:\r\n\r\nBusiness Type: {business_type}\r\nPrimary Keywords: {keywords}\r\nTarget Audience: {audience}\r\nGoals: {goals}\r\n\r\nInstructions:\r\n1. Create a logical, user-friendly navigation hierarchy\r\n2. Include essential pages (Home, About, Services/Products, Contact)\r\n3. Design category pages for primary keywords\r\n4. Plan supporting content pages\r\n5. Consider user journey and conversion paths\r\n\r\nReturn a JSON structure:\r\n{\r\n \"navigation\": [\r\n {\r\n \"page\": \"Page name\",\r\n \"slug\": \"url-slug\",\r\n \"type\": \"home|category|product|service|content|utility\",\r\n \"children\": []\r\n }\r\n ]\r\n}",
+ "description": "",
+ "variables": [],
+ "is_active": false,
+ "version": 1,
+ "last_updated": "2025-12-20T19:58:33.034Z",
+ "created_at": "2025-12-20T12:53:51.914Z"
+ }
+},
+{
+ "model": "system.globalaiprompt",
+ "pk": 10,
+ "fields": {
+ "prompt_type": "taxonomy_generation",
+ "prompt_value": "Create a logical taxonomy structure for:\r\n\r\nContent Type: {content_type}\r\nDomain: {domain}\r\nExisting Keywords: {keywords}\r\n\r\nInstructions:\r\n1. Design parent categories that organize content logically\r\n2. Create subcategories for detailed organization\r\n3. Ensure balanced hierarchy (not too deep or flat)\r\n4. Use clear, descriptive category names\r\n5. Consider SEO and user navigation\r\n\r\nReturn a JSON structure:\r\n{\r\n \"categories\": [\r\n {\r\n \"name\": \"Category Name\",\r\n \"slug\": \"category-slug\",\r\n \"description\": \"Brief description\",\r\n \"subcategories\": []\r\n }\r\n ]\r\n}",
+ "description": "",
+ "variables": [],
+ "is_active": false,
+ "version": 1,
+ "last_updated": "2025-12-20T19:58:48.350Z",
+ "created_at": "2025-12-20T12:53:51.922Z"
+ }
+}
+]
diff --git a/backups/config/20260113/industries.json b/backups/config/20260113/industries.json
new file mode 100644
index 00000000..a4fa04ea
--- /dev/null
+++ b/backups/config/20260113/industries.json
@@ -0,0 +1,158 @@
+[
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 22,
+ "fields": {
+ "name": "Apparel & Fashion",
+ "slug": "apparel-fashion",
+ "description": "Fashion, clothing, and apparel industry",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.128Z",
+ "updated_at": "2025-11-19T20:57:22.128Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 9,
+ "fields": {
+ "name": "Automotive",
+ "slug": "automotive",
+ "description": "Automotive sales, services, and parts",
+ "is_active": true,
+ "created_at": "2025-11-04T16:43:57.026Z",
+ "updated_at": "2025-11-19T20:57:22.146Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 23,
+ "fields": {
+ "name": "Beauty & Personal Care",
+ "slug": "beauty-personal-care",
+ "description": "Beauty, skincare, and personal care products and services",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.140Z",
+ "updated_at": "2025-11-19T20:57:22.141Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 29,
+ "fields": {
+ "name": "Education & Training",
+ "slug": "education-training",
+ "description": "Educational institutions, training programs, and learning services",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.190Z",
+ "updated_at": "2025-11-19T20:57:22.190Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 10,
+ "fields": {
+ "name": "Fashion & Apparel",
+ "slug": "fashion-apparel",
+ "description": "Fashion, clothing, and apparel businesses",
+ "is_active": false,
+ "created_at": "2025-11-04T16:43:57.035Z",
+ "updated_at": "2025-11-19T21:05:19.935Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 28,
+ "fields": {
+ "name": "Finance & Insurance",
+ "slug": "finance-insurance",
+ "description": "Financial services, banking, insurance, and investment",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.182Z",
+ "updated_at": "2025-11-19T20:57:22.182Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 7,
+ "fields": {
+ "name": "Food & Beverage",
+ "slug": "food-beverage",
+ "description": "Restaurants, food services, and beverage industry",
+ "is_active": true,
+ "created_at": "2025-11-04T16:43:57.006Z",
+ "updated_at": "2025-11-19T20:57:22.196Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 25,
+ "fields": {
+ "name": "Healthcare & Medical",
+ "slug": "healthcare-medical",
+ "description": "Healthcare services, medical practices, and health-related services",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.161Z",
+ "updated_at": "2025-11-19T20:57:22.161Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 24,
+ "fields": {
+ "name": "Home & Furniture",
+ "slug": "home-furniture",
+ "description": "Furniture, home decor, and home improvement",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.155Z",
+ "updated_at": "2025-11-19T20:57:22.155Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 21,
+ "fields": {
+ "name": "Home & Garden",
+ "slug": "home-garden",
+ "description": "Home improvement, gardening, landscaping, and interior design",
+ "is_active": true,
+ "created_at": "2025-11-05T11:07:20.331Z",
+ "updated_at": "2025-11-05T11:07:20.331Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 26,
+ "fields": {
+ "name": "Real Estate & Construction",
+ "slug": "real-estate-construction",
+ "description": "Real estate, property management, and construction services",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.168Z",
+ "updated_at": "2025-11-19T20:57:22.168Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 1,
+ "fields": {
+ "name": "Technology",
+ "slug": "technology",
+ "description": "Software, cloud computing, cybersecurity, and technology services",
+ "is_active": false,
+ "created_at": "2025-11-04T14:56:39.099Z",
+ "updated_at": "2025-11-19T21:05:19.931Z"
+ }
+},
+{
+ "model": "igny8_core_auth.industry",
+ "pk": 27,
+ "fields": {
+ "name": "Technology & IT Services",
+ "slug": "technology-it-services",
+ "description": "Technology services, software development, and IT solutions",
+ "is_active": true,
+ "created_at": "2025-11-19T20:57:22.175Z",
+ "updated_at": "2025-11-19T20:57:22.175Z"
+ }
+}
+]
diff --git a/backups/config/20260113/plans.json b/backups/config/20260113/plans.json
new file mode 100644
index 00000000..1d0bac75
--- /dev/null
+++ b/backups/config/20260113/plans.json
@@ -0,0 +1,191 @@
+[
+{
+ "model": "igny8_core_auth.plan",
+ "pk": 6,
+ "fields": {
+ "name": "Internal (System/Superuser)",
+ "slug": "internal",
+ "price": "0.00",
+ "original_price": null,
+ "billing_cycle": "monthly",
+ "annual_discount_percent": 15,
+ "is_featured": false,
+ "features": [
+ "ai_writer",
+ "image_gen",
+ "auto_publish",
+ "custom_prompts",
+ "unlimited"
+ ],
+ "is_active": true,
+ "is_internal": true,
+ "created_at": "2025-11-08T23:14:16.130Z",
+ "max_users": 10000,
+ "max_sites": 20,
+ "max_industries": null,
+ "max_author_profiles": 5,
+ "max_keywords": 100000,
+ "max_ahrefs_queries": 500,
+ "included_credits": 10000,
+ "extra_credit_price": "0.01",
+ "allow_credit_topup": true,
+ "auto_credit_topup_threshold": null,
+ "auto_credit_topup_amount": null,
+ "stripe_product_id": null,
+ "stripe_price_id": null,
+ "credits_per_month": 2000
+ }
+},
+{
+ "model": "igny8_core_auth.plan",
+ "pk": 1,
+ "fields": {
+ "name": "Free Plan",
+ "slug": "free",
+ "price": "0.00",
+ "original_price": null,
+ "billing_cycle": "monthly",
+ "annual_discount_percent": 15,
+ "is_featured": false,
+ "features": [
+ "1 Site",
+ "50 Keywords",
+ "100 Credits",
+ "10K Words",
+ "10 Clusters",
+ "20 Images"
+ ],
+ "is_active": false,
+ "is_internal": false,
+ "created_at": "2025-11-02T22:01:17.053Z",
+ "max_users": 1,
+ "max_sites": 1,
+ "max_industries": 1,
+ "max_author_profiles": 2,
+ "max_keywords": 100,
+ "max_ahrefs_queries": 0,
+ "included_credits": 100,
+ "extra_credit_price": "0.00",
+ "allow_credit_topup": false,
+ "auto_credit_topup_threshold": null,
+ "auto_credit_topup_amount": null,
+ "stripe_product_id": null,
+ "stripe_price_id": null,
+ "credits_per_month": 0
+ }
+},
+{
+ "model": "igny8_core_auth.plan",
+ "pk": 2,
+ "fields": {
+ "name": "Starter",
+ "slug": "starter",
+ "price": "99.00",
+ "original_price": null,
+ "billing_cycle": "monthly",
+ "annual_discount_percent": 10,
+ "is_featured": false,
+ "features": [
+ "50 Pages/Articles",
+ "2 Sites",
+ "2 Team Users",
+ "500 Keywords",
+ "200 Images",
+ "AI SEO"
+ ],
+ "is_active": true,
+ "is_internal": false,
+ "created_at": "2025-11-04T14:55:33.381Z",
+ "max_users": 2,
+ "max_sites": 2,
+ "max_industries": null,
+ "max_author_profiles": 5,
+ "max_keywords": 500,
+ "max_ahrefs_queries": 50,
+ "included_credits": 1000,
+ "extra_credit_price": "0.10",
+ "allow_credit_topup": true,
+ "auto_credit_topup_threshold": null,
+ "auto_credit_topup_amount": null,
+ "stripe_product_id": "prod_TkGrgKWbrBo4sX",
+ "stripe_price_id": "price_1SmmJhPdRe4dWeLwSufhikqW",
+ "credits_per_month": 1000
+ }
+},
+{
+ "model": "igny8_core_auth.plan",
+ "pk": 4,
+ "fields": {
+ "name": "Growth",
+ "slug": "growth",
+ "price": "199.00",
+ "original_price": null,
+ "billing_cycle": "monthly",
+ "annual_discount_percent": 10,
+ "is_featured": true,
+ "features": [
+ "200 Pages/Articles",
+ "5 Sites",
+ "3 Team Users",
+ "2000 Keywords",
+ "800 Images",
+ "AI SEO"
+ ],
+ "is_active": true,
+ "is_internal": false,
+ "created_at": "2025-11-07T11:46:29.144Z",
+ "max_users": 3,
+ "max_sites": 5,
+ "max_industries": null,
+ "max_author_profiles": 5,
+ "max_keywords": 2000,
+ "max_ahrefs_queries": 200,
+ "included_credits": 2000,
+ "extra_credit_price": "0.08",
+ "allow_credit_topup": true,
+ "auto_credit_topup_threshold": null,
+ "auto_credit_topup_amount": null,
+ "stripe_product_id": "prod_TkGsliLPq7Sl8w",
+ "stripe_price_id": "price_1SmmKMPdRe4dWeLw9nfQUVus",
+ "credits_per_month": 0
+ }
+},
+{
+ "model": "igny8_core_auth.plan",
+ "pk": 5,
+ "fields": {
+ "name": "Scale",
+ "slug": "scale",
+ "price": "299.00",
+ "original_price": null,
+ "billing_cycle": "monthly",
+ "annual_discount_percent": 10,
+ "is_featured": false,
+ "features": [
+ "500 Pages/Articles",
+ "99 Sites",
+ "5 Team Users",
+ "5000 Keywords",
+ "2000 Images",
+ "AI SEO"
+ ],
+ "is_active": true,
+ "is_internal": false,
+ "created_at": "2025-11-07T11:46:29.148Z",
+ "max_users": 5,
+ "max_sites": 99,
+ "max_industries": null,
+ "max_author_profiles": 10,
+ "max_keywords": 5000,
+ "max_ahrefs_queries": 500,
+ "included_credits": 5000,
+ "extra_credit_price": "0.06",
+ "allow_credit_topup": true,
+ "auto_credit_topup_threshold": null,
+ "auto_credit_topup_amount": null,
+ "stripe_product_id": "prod_TkGs64iCjpb9Ok",
+ "stripe_price_id": "price_1SmmL6PdRe4dWeLwisBUvC0N",
+ "credits_per_month": 0
+ }
+}
+]
diff --git a/backups/config/20260113/prompts.json b/backups/config/20260113/prompts.json
new file mode 100644
index 00000000..a4284d00
--- /dev/null
+++ b/backups/config/20260113/prompts.json
@@ -0,0 +1,16 @@
+[
+{
+ "model": "system.aiprompt",
+ "pk": 17,
+ "fields": {
+ "account": 90,
+ "prompt_type": "clustering",
+ "prompt_value": "# Semantic Authority Grid - Keyword Clustering Engine\r\n\r\nYou are a semantic strategist building **topic ecosystems** using the Semantic Authority Grid methodology. Analyze keywords and group them into **mini-ecosystems** where each cluster represents a complete, self-contained topic authority area.\r\n\r\n---\r\n\r\n## CORE PRINCIPLE\r\n\r\n**Clusters are Mini-Ecosystems**\r\n\r\nEach cluster must function as a complete topical authority unit containing:\r\n- 1 root/anchor topic (hub page potential)\r\n- Supporting semantic variations (blog topics)\r\n- Natural attribute dimensions (filters/taxonomies)\r\n- Clear user journey pathways\r\n- ignore volume & difficulty provided\r\n---\r\n\r\n## Keywords to Cluster\r\n\r\n{\r\n \"keywords\": [IGNY8_KEYWORDS]\r\n}\r\n\r\n---\r\n\r\n## OUTPUT FORMAT JSON Object\r\n\r\nReturn ONLY valid JSON with no explanations or commentary:\r\n\r\n{\r\n \"clusters\": [\r\n {\r\n \"name\": \"...\",\r\n \"description\": \"...\",\r\n \"keywords\": [\"...\", \"...\", \"...\"]\r\n }\r\n ]\r\n}\r\n\r\n---\r\n\r\n## CLUSTERING METHODOLOGY\r\n\r\n### Step 1: Identify Natural Semantic Overlaps\r\n\r\nKeywords cluster together when they share **2+ dimensional intersections**:\r\n\r\n**Dimensional Framework:**\r\n1. **Topic/Subject** → The core thing being discussed\r\n2. **Problem/Need** → What pain point or desire drives the search\r\n3. **Solution/Method** → How something is done or achieved\r\n4. **Feature/Attribute** → Specific characteristics (heated, waterproof, organic, portable)\r\n5. **Persona/Audience** → Who needs this (beginners, professionals, age groups, conditions)\r\n6. **Use-Case/Application** → Where/when/why it's used (pregnancy, travel, office, summer)\r\n7. **Product Type/Format** → Category or form factor (serum, massager, organizer, sheets)\r\n8. **Comparison/Alternative** → Explicit or implied comparison queries\r\n9. **Context/Modifier** → Geographic, temporal, or situational qualifiers\r\n\r\n**Example of Multi-Dimensional Intersection:**\r\n- \"best foot massagers for plantar fasciitis\"\r\n - Dimensions: Product Type (foot massager) + Persona (plantar fasciitis sufferers) + Problem (pain relief)\r\n- \"heated shiatsu back massager\"\r\n - Dimensions: Product Type (back massager) + Feature (heated) + Feature (shiatsu)\r\n- \"are back massagers safe during pregnancy\"\r\n - Dimensions: Product Type (back massager) + Persona (pregnant women) + Problem (safety concern)\r\n\r\nThese belong in the same cluster because they share **Product Type dimension** and serve **interconnected user journeys**.\r\n\r\n---\r\n\r\n### Step 2: Map User Journey Patterns\r\n\r\nEach cluster should support natural user exploration paths:\r\n\r\n**Journey Patterns:**\r\n- **Problem → Information → Solution → Product**\r\n - \"what causes plantar fasciitis\" → \"best treatments for plantar fasciitis\" → \"foot massagers for plantar fasciitis\"\r\n \r\n- **General → Specific → Variant**\r\n - \"foot massagers\" → \"shiatsu foot massagers\" → \"heated shiatsu foot massagers\"\r\n \r\n- **Question → Explanation → Options → Decision**\r\n - \"do vitamin c serums work\" → \"how vitamin c brightens skin\" → \"best vitamin c serums\" → \"vitamin c vs retinol\"\r\n \r\n- **Feature Discovery → Feature Comparison → Feature + Use-Case**\r\n - \"heated massagers\" → \"heated vs unheated massagers\" → \"heated massagers for arthritis\"\r\n\r\n**Cluster Test:** Can a user naturally move from any keyword in the cluster to any other keyword in a logical research/buying journey?\r\n\r\n---\r\n\r\n### Step 3: Identify Cluster Anchors (Hub Potential)\r\n\r\nEach cluster needs a clear **root keyword** that can serve as the hub page:\r\n\r\n**Hub Characteristics:**\r\n- Broad enough to encompass all cluster keywords\r\n- Specific enough to be commercially or informationally valuable\r\n- Natural landing point for user journey\r\n- Can support 3-10+ supporting content pieces\r\n\r\n**Examples:**\r\n- ✅ \"foot massagers for plantar fasciitis\" (hub)\r\n - Supports: safety concerns, specific features, comparisons, use-cases\r\n- ✅ \"vitamin c serums for brightening\" (hub)\r\n - Supports: vs alternatives, for skin types, application guides, safety\r\n- ❌ \"massagers\" (too broad, no natural ecosystem)\r\n- ❌ \"renpho foot massager cord length\" (too narrow, can't support ecosystem)\r\n\r\n---\r\n\r\n### Step 4: Extract Attribute Dimensions\r\n\r\nFor each cluster, identify **taxonomy-worthy dimensions** that could become:\r\n- Product filters\r\n- Content tags\r\n- URL parameters\r\n- Faceted navigation\r\n\r\n**Extraction Method:**\r\nLook for recurring **modifiers** across cluster keywords:\r\n- Material: cotton, linen, silk, organic\r\n- Feature: heated, waterproof, portable, wireless\r\n- Size: king, queen, twin, compact\r\n- Type: shiatsu, percussion, vibration, EMS\r\n- Audience: men, women, seniors, athletes\r\n- Context: travel, home, office, car\r\n\r\nThese dimensions should appear in the cluster's description as **natural grouping signals**.\r\n\r\n---\r\n\r\n## CLUSTERING RULES\r\n\r\n### Formation Rules\r\n\r\n1. **Semantic Coherence**\r\n - Keywords must share meaningful semantic relationships\r\n - Shared words alone don't make a cluster\r\n - \"back massager\" + \"back support pillow\" = different clusters (different product ecosystems)\r\n\r\n2. **Dimensional Intersection**\r\n - Minimum 2 shared dimensions required\r\n - More intersections = stronger cluster\r\n - Keywords with 3+ shared dimensions are core cluster members\r\n\r\n3. **User Journey Viability**\r\n - Cluster keywords should form natural navigation paths\r\n - Hub → supporting blogs → variants should flow logically\r\n - Test: Would a user exploring this topic naturally encounter all these keywords?\r\n\r\n4. **Ecosystem Completeness**\r\n - Each cluster should be self-contained\r\n - Should support 1 hub page + 3-10 supporting articles\r\n - Must have enough depth for authority building\r\n\r\n5. **Exclusion Over Inclusion**\r\n - Better to leave keywords unclustered than force weak groupings\r\n - Outliers should be excluded from output\r\n - Only cluster keywords with strong semantic bonds\r\n\r\n### Size & Quality Rules\r\n\r\n- **Minimum cluster size:** 3 keywords (otherwise it's not an ecosystem)\r\n- **Maximum cluster size:** 15 keywords (beyond this, consider splitting)\r\n- **Optimal cluster size:** 5-10 keywords (hub + supporting + variants)\r\n- **No keyword duplication:** Each keyword appears in exactly one cluster\r\n- **Quality over quantity:** 5 strong clusters > 15 weak clusters\r\n\r\n---\r\n\r\n## OUTPUT STRUCTURE json object\r\n{\r\n \"clusters\": [\r\n {\r\n \"name\": \"[Natural, SEO-relevant cluster name representing the root topic]\",\r\n \"description\": \"[2-3 sentences explaining: (1) what semantic dimensions bind these keywords, (2) what user journey or problem space this cluster addresses]\",\r\n \"keywords\": [\"keyword 1\", \"keyword 2\", \"keyword 3\", \"...\"]\r\n }\r\n ]\r\n}\r\n\r\n\r\n### Naming Guidelines\r\n\r\n**Good Cluster Names:**\r\n- \"Foot Massagers for Plantar Fasciitis Treatment\"\r\n- \"Organic Cotton Bedding for Summer\"\r\n- \"Vitamin C Serums for Skin Brightening\"\r\n- \"Waterproof Car Trunk Organizers\"\r\n\r\n**Poor Cluster Names:**\r\n- \"Massagers\" (too broad)\r\n- \"Products\" (meaningless)\r\n- \"Heated\" (just a feature, not a topic)\r\n- \"Queries about safety\" (meta, not topical)\r\n\r\n### Description Guidelines\r\n\r\n**Good Description Format:**\r\n\"This cluster covers [TOPIC] focused on [PROBLEM/USE-CASE] for [AUDIENCE]. Keywords share dimensions of [DIMENSION 1] and [DIMENSION 2], forming a natural ecosystem for users researching [USER JOURNEY].\"\r\n\r\n**Example:**\r\n\"This cluster covers foot massage devices specifically for plantar fasciitis relief. Keywords share dimensions of product type (foot massager), health condition (plantar fasciitis), and therapeutic features (shiatsu, heated, EMS), forming a natural ecosystem for users researching pain relief solutions and comparing treatment options.\"\r\n\r\n---\r\n\r\n## VALIDATION CHECKLIST\r\n\r\nBefore finalizing clusters, verify:\r\n\r\n✓ **Hub Potential:** Each cluster has 1 clear anchor keyword for hub page\r\n✓ **Dimensional Overlap:** Keywords share 2+ semantic dimensions\r\n✓ **User Journey:** Natural navigation paths exist within cluster\r\n✓ **Attribute Dimensions:** Recurring modifiers can become filters/taxonomies\r\n✓ **Ecosystem Completeness:** Supports 1 hub + 3-10 supporting articles\r\n✓ **Semantic Coherence:** Keywords genuinely belong together, not just word overlap\r\n✓ **No Duplication:** Each keyword appears once\r\n✓ **No Weak Clusters:** Only include strong, viable ecosystems\r\n\r\n---\r\n\r\n## ANTI-PATTERNS TO AVOID\r\n\r\n❌ **Single-word grouping:** \"All keywords with 'heated' go together\"\r\n❌ **Forced categorization:** \"Must fit everything into clusters\"\r\n❌ **Shallow connections:** Keywords share one word but no semantic relationship\r\n❌ **Traditional SEO intent labels:** Don't use \"informational\" or \"commercial\" as clustering logic\r\n❌ **Assumed site structure:** Don't cluster based on existing categories\r\n❌ **Word-matching only:** \"massage\" keywords together regardless of context\r\n\r\n✅ **Multi-dimensional analysis:** Find deep semantic intersections\r\n✅ **User journey modeling:** Natural exploration and research paths\r\n✅ **Ecosystem thinking:** Self-contained topical authority units\r\n✅ **Attribute extraction:** Recurring dimensions that create taxonomies\r\n✅ **Quality filtering:** Only strong, viable clusters in output\r\n\r\n---\r\n\r\n## EXAMPLES\r\n\r\n### Example 1: Strong Cluster\r\n\r\n{\r\n \"name\": \"Heated Shiatsu Back Massagers for Pain Relief\",\r\n \"description\": \"This cluster covers back massage devices combining heated therapy with shiatsu technique for pain management. Keywords intersect on product type (back massager), features (heated, shiatsu), and problem-solving (pain relief, muscle tension), supporting a complete user journey from problem awareness to product comparison.\",\r\n \"keywords\": [\r\n \"heated back massagers\",\r\n \"shiatsu back massager with heat\",\r\n \"best heated massagers for back pain\",\r\n \"do heated massagers help muscle pain\",\r\n \"heated vs unheated back massagers\",\r\n \"shiatsu massage for lower back pain\"\r\n ]\r\n}\r\n\r\n\r\n### Example 2: Multi-Dimensional Cluster\r\n\r\n{\r\n \"name\": \"Organic Cotton Bed Sheets for Hot Sleepers\",\r\n \"description\": \"This cluster addresses cooling bedding solutions using organic materials. Keywords intersect on material (organic cotton), product type (bed sheets), use-case (hot weather/hot sleepers), and benefit (breathability, cooling), forming an ecosystem for users researching temperature-regulating sleep solutions.\",\r\n \"keywords\": [\r\n \"organic cotton sheets for summer\",\r\n \"best cooling cotton bed sheets\",\r\n \"breathable organic cotton bedding\",\r\n \"cotton vs linen sheets for hot sleepers\",\r\n \"organic cotton king size sheets\",\r\n \"are cotton sheets good for hot weather\"\r\n ]\r\n}\r\n\r\n\r\n### Example 3: What NOT to Cluster\r\n\r\n❌ BAD CLUSTER:\r\n{\r\n \"name\": \"Massager Products\",\r\n \"keywords\": [\r\n \"foot massagers\",\r\n \"back massagers\", \r\n \"neck massagers\",\r\n \"massage oils\",\r\n \"massage chairs\"\r\n ]\r\n}\r\n\r\n**Why it's bad:** Keywords share only the word \"massage\" but represent completely different product ecosystems with different user journeys, problems, and attributes. These should be separate clusters.",
+ "default_prompt": "# Semantic Authority Grid - Keyword Clustering Engine\r\n\r\nYou are a semantic strategist building **topic ecosystems** using the Semantic Authority Grid methodology. Analyze keywords and group them into **mini-ecosystems** where each cluster represents a complete, self-contained topic authority area.\r\n\r\n---\r\n\r\n## CORE PRINCIPLE\r\n\r\n**Clusters are Mini-Ecosystems**\r\n\r\nEach cluster must function as a complete topical authority unit containing:\r\n- 1 root/anchor topic (hub page potential)\r\n- Supporting semantic variations (blog topics)\r\n- Natural attribute dimensions (filters/taxonomies)\r\n- Clear user journey pathways\r\n- ignore volume & difficulty provided\r\n---\r\n\r\n## Keywords to Cluster\r\n\r\n{\r\n \"keywords\": [IGNY8_KEYWORDS]\r\n}\r\n\r\n---\r\n\r\n## OUTPUT FORMAT JSON Object\r\n\r\nReturn ONLY valid JSON with no explanations or commentary:\r\n\r\n{\r\n \"clusters\": [\r\n {\r\n \"name\": \"...\",\r\n \"description\": \"...\",\r\n \"keywords\": [\"...\", \"...\", \"...\"]\r\n }\r\n ]\r\n}\r\n\r\n---\r\n\r\n## CLUSTERING METHODOLOGY\r\n\r\n### Step 1: Identify Natural Semantic Overlaps\r\n\r\nKeywords cluster together when they share **2+ dimensional intersections**:\r\n\r\n**Dimensional Framework:**\r\n1. **Topic/Subject** → The core thing being discussed\r\n2. **Problem/Need** → What pain point or desire drives the search\r\n3. **Solution/Method** → How something is done or achieved\r\n4. **Feature/Attribute** → Specific characteristics (heated, waterproof, organic, portable)\r\n5. **Persona/Audience** → Who needs this (beginners, professionals, age groups, conditions)\r\n6. **Use-Case/Application** → Where/when/why it's used (pregnancy, travel, office, summer)\r\n7. **Product Type/Format** → Category or form factor (serum, massager, organizer, sheets)\r\n8. **Comparison/Alternative** → Explicit or implied comparison queries\r\n9. **Context/Modifier** → Geographic, temporal, or situational qualifiers\r\n\r\n**Example of Multi-Dimensional Intersection:**\r\n- \"best foot massagers for plantar fasciitis\"\r\n - Dimensions: Product Type (foot massager) + Persona (plantar fasciitis sufferers) + Problem (pain relief)\r\n- \"heated shiatsu back massager\"\r\n - Dimensions: Product Type (back massager) + Feature (heated) + Feature (shiatsu)\r\n- \"are back massagers safe during pregnancy\"\r\n - Dimensions: Product Type (back massager) + Persona (pregnant women) + Problem (safety concern)\r\n\r\nThese belong in the same cluster because they share **Product Type dimension** and serve **interconnected user journeys**.\r\n\r\n---\r\n\r\n### Step 2: Map User Journey Patterns\r\n\r\nEach cluster should support natural user exploration paths:\r\n\r\n**Journey Patterns:**\r\n- **Problem → Information → Solution → Product**\r\n - \"what causes plantar fasciitis\" → \"best treatments for plantar fasciitis\" → \"foot massagers for plantar fasciitis\"\r\n \r\n- **General → Specific → Variant**\r\n - \"foot massagers\" → \"shiatsu foot massagers\" → \"heated shiatsu foot massagers\"\r\n \r\n- **Question → Explanation → Options → Decision**\r\n - \"do vitamin c serums work\" → \"how vitamin c brightens skin\" → \"best vitamin c serums\" → \"vitamin c vs retinol\"\r\n \r\n- **Feature Discovery → Feature Comparison → Feature + Use-Case**\r\n - \"heated massagers\" → \"heated vs unheated massagers\" → \"heated massagers for arthritis\"\r\n\r\n**Cluster Test:** Can a user naturally move from any keyword in the cluster to any other keyword in a logical research/buying journey?\r\n\r\n---\r\n\r\n### Step 3: Identify Cluster Anchors (Hub Potential)\r\n\r\nEach cluster needs a clear **root keyword** that can serve as the hub page:\r\n\r\n**Hub Characteristics:**\r\n- Broad enough to encompass all cluster keywords\r\n- Specific enough to be commercially or informationally valuable\r\n- Natural landing point for user journey\r\n- Can support 3-10+ supporting content pieces\r\n\r\n**Examples:**\r\n- ✅ \"foot massagers for plantar fasciitis\" (hub)\r\n - Supports: safety concerns, specific features, comparisons, use-cases\r\n- ✅ \"vitamin c serums for brightening\" (hub)\r\n - Supports: vs alternatives, for skin types, application guides, safety\r\n- ❌ \"massagers\" (too broad, no natural ecosystem)\r\n- ❌ \"renpho foot massager cord length\" (too narrow, can't support ecosystem)\r\n\r\n---\r\n\r\n### Step 4: Extract Attribute Dimensions\r\n\r\nFor each cluster, identify **taxonomy-worthy dimensions** that could become:\r\n- Product filters\r\n- Content tags\r\n- URL parameters\r\n- Faceted navigation\r\n\r\n**Extraction Method:**\r\nLook for recurring **modifiers** across cluster keywords:\r\n- Material: cotton, linen, silk, organic\r\n- Feature: heated, waterproof, portable, wireless\r\n- Size: king, queen, twin, compact\r\n- Type: shiatsu, percussion, vibration, EMS\r\n- Audience: men, women, seniors, athletes\r\n- Context: travel, home, office, car\r\n\r\nThese dimensions should appear in the cluster's description as **natural grouping signals**.\r\n\r\n---\r\n\r\n## CLUSTERING RULES\r\n\r\n### Formation Rules\r\n\r\n1. **Semantic Coherence**\r\n - Keywords must share meaningful semantic relationships\r\n - Shared words alone don't make a cluster\r\n - \"back massager\" + \"back support pillow\" = different clusters (different product ecosystems)\r\n\r\n2. **Dimensional Intersection**\r\n - Minimum 2 shared dimensions required\r\n - More intersections = stronger cluster\r\n - Keywords with 3+ shared dimensions are core cluster members\r\n\r\n3. **User Journey Viability**\r\n - Cluster keywords should form natural navigation paths\r\n - Hub → supporting blogs → variants should flow logically\r\n - Test: Would a user exploring this topic naturally encounter all these keywords?\r\n\r\n4. **Ecosystem Completeness**\r\n - Each cluster should be self-contained\r\n - Should support 1 hub page + 3-10 supporting articles\r\n - Must have enough depth for authority building\r\n\r\n5. **Exclusion Over Inclusion**\r\n - Better to leave keywords unclustered than force weak groupings\r\n - Outliers should be excluded from output\r\n - Only cluster keywords with strong semantic bonds\r\n\r\n### Size & Quality Rules\r\n\r\n- **Minimum cluster size:** 3 keywords (otherwise it's not an ecosystem)\r\n- **Maximum cluster size:** 15 keywords (beyond this, consider splitting)\r\n- **Optimal cluster size:** 5-10 keywords (hub + supporting + variants)\r\n- **No keyword duplication:** Each keyword appears in exactly one cluster\r\n- **Quality over quantity:** 5 strong clusters > 15 weak clusters\r\n\r\n---\r\n\r\n## OUTPUT STRUCTURE json object\r\n{\r\n \"clusters\": [\r\n {\r\n \"name\": \"[Natural, SEO-relevant cluster name representing the root topic]\",\r\n \"description\": \"[2-3 sentences explaining: (1) what semantic dimensions bind these keywords, (2) what user journey or problem space this cluster addresses]\",\r\n \"keywords\": [\"keyword 1\", \"keyword 2\", \"keyword 3\", \"...\"]\r\n }\r\n ]\r\n}\r\n\r\n\r\n### Naming Guidelines\r\n\r\n**Good Cluster Names:**\r\n- \"Foot Massagers for Plantar Fasciitis Treatment\"\r\n- \"Organic Cotton Bedding for Summer\"\r\n- \"Vitamin C Serums for Skin Brightening\"\r\n- \"Waterproof Car Trunk Organizers\"\r\n\r\n**Poor Cluster Names:**\r\n- \"Massagers\" (too broad)\r\n- \"Products\" (meaningless)\r\n- \"Heated\" (just a feature, not a topic)\r\n- \"Queries about safety\" (meta, not topical)\r\n\r\n### Description Guidelines\r\n\r\n**Good Description Format:**\r\n\"This cluster covers [TOPIC] focused on [PROBLEM/USE-CASE] for [AUDIENCE]. Keywords share dimensions of [DIMENSION 1] and [DIMENSION 2], forming a natural ecosystem for users researching [USER JOURNEY].\"\r\n\r\n**Example:**\r\n\"This cluster covers foot massage devices specifically for plantar fasciitis relief. Keywords share dimensions of product type (foot massager), health condition (plantar fasciitis), and therapeutic features (shiatsu, heated, EMS), forming a natural ecosystem for users researching pain relief solutions and comparing treatment options.\"\r\n\r\n---\r\n\r\n## VALIDATION CHECKLIST\r\n\r\nBefore finalizing clusters, verify:\r\n\r\n✓ **Hub Potential:** Each cluster has 1 clear anchor keyword for hub page\r\n✓ **Dimensional Overlap:** Keywords share 2+ semantic dimensions\r\n✓ **User Journey:** Natural navigation paths exist within cluster\r\n✓ **Attribute Dimensions:** Recurring modifiers can become filters/taxonomies\r\n✓ **Ecosystem Completeness:** Supports 1 hub + 3-10 supporting articles\r\n✓ **Semantic Coherence:** Keywords genuinely belong together, not just word overlap\r\n✓ **No Duplication:** Each keyword appears once\r\n✓ **No Weak Clusters:** Only include strong, viable ecosystems\r\n\r\n---\r\n\r\n## ANTI-PATTERNS TO AVOID\r\n\r\n❌ **Single-word grouping:** \"All keywords with 'heated' go together\"\r\n❌ **Forced categorization:** \"Must fit everything into clusters\"\r\n❌ **Shallow connections:** Keywords share one word but no semantic relationship\r\n❌ **Traditional SEO intent labels:** Don't use \"informational\" or \"commercial\" as clustering logic\r\n❌ **Assumed site structure:** Don't cluster based on existing categories\r\n❌ **Word-matching only:** \"massage\" keywords together regardless of context\r\n\r\n✅ **Multi-dimensional analysis:** Find deep semantic intersections\r\n✅ **User journey modeling:** Natural exploration and research paths\r\n✅ **Ecosystem thinking:** Self-contained topical authority units\r\n✅ **Attribute extraction:** Recurring dimensions that create taxonomies\r\n✅ **Quality filtering:** Only strong, viable clusters in output\r\n\r\n---\r\n\r\n## EXAMPLES\r\n\r\n### Example 1: Strong Cluster\r\n\r\n{\r\n \"name\": \"Heated Shiatsu Back Massagers for Pain Relief\",\r\n \"description\": \"This cluster covers back massage devices combining heated therapy with shiatsu technique for pain management. Keywords intersect on product type (back massager), features (heated, shiatsu), and problem-solving (pain relief, muscle tension), supporting a complete user journey from problem awareness to product comparison.\",\r\n \"keywords\": [\r\n \"heated back massagers\",\r\n \"shiatsu back massager with heat\",\r\n \"best heated massagers for back pain\",\r\n \"do heated massagers help muscle pain\",\r\n \"heated vs unheated back massagers\",\r\n \"shiatsu massage for lower back pain\"\r\n ]\r\n}\r\n\r\n\r\n### Example 2: Multi-Dimensional Cluster\r\n\r\n{\r\n \"name\": \"Organic Cotton Bed Sheets for Hot Sleepers\",\r\n \"description\": \"This cluster addresses cooling bedding solutions using organic materials. Keywords intersect on material (organic cotton), product type (bed sheets), use-case (hot weather/hot sleepers), and benefit (breathability, cooling), forming an ecosystem for users researching temperature-regulating sleep solutions.\",\r\n \"keywords\": [\r\n \"organic cotton sheets for summer\",\r\n \"best cooling cotton bed sheets\",\r\n \"breathable organic cotton bedding\",\r\n \"cotton vs linen sheets for hot sleepers\",\r\n \"organic cotton king size sheets\",\r\n \"are cotton sheets good for hot weather\"\r\n ]\r\n}\r\n\r\n\r\n### Example 3: What NOT to Cluster\r\n\r\n❌ BAD CLUSTER:\r\n{\r\n \"name\": \"Massager Products\",\r\n \"keywords\": [\r\n \"foot massagers\",\r\n \"back massagers\", \r\n \"neck massagers\",\r\n \"massage oils\",\r\n \"massage chairs\"\r\n ]\r\n}\r\n\r\n**Why it's bad:** Keywords share only the word \"massage\" but represent completely different product ecosystems with different user journeys, problems, and attributes. These should be separate clusters.",
+ "is_customized": false,
+ "is_active": true,
+ "updated_at": "2025-12-20T20:09:13.413Z",
+ "created_at": "2025-12-20T20:04:43.984Z"
+ }
+}
+]
diff --git a/backups/config/20260113/sectors.json b/backups/config/20260113/sectors.json
new file mode 100644
index 00000000..dbbf4b88
--- /dev/null
+++ b/backups/config/20260113/sectors.json
@@ -0,0 +1,86 @@
+[
+{
+ "model": "igny8_core_auth.sector",
+ "pk": 50,
+ "fields": {
+ "is_deleted": false,
+ "deleted_at": null,
+ "restore_until": null,
+ "delete_reason": null,
+ "deleted_by": null,
+ "account": 90,
+ "site": 21,
+ "industry_sector": 68,
+ "name": "Footwear",
+ "slug": "footwear",
+ "description": "",
+ "is_active": false,
+ "status": "active",
+ "created_at": "2025-12-10T07:51:10.139Z",
+ "updated_at": "2025-12-10T07:51:10.139Z"
+ }
+},
+{
+ "model": "igny8_core_auth.sector",
+ "pk": 51,
+ "fields": {
+ "is_deleted": false,
+ "deleted_at": null,
+ "restore_until": null,
+ "delete_reason": null,
+ "deleted_by": null,
+ "account": 90,
+ "site": 21,
+ "industry_sector": 66,
+ "name": "Kidswear",
+ "slug": "kidswear",
+ "description": "",
+ "is_active": false,
+ "status": "active",
+ "created_at": "2025-12-10T07:51:10.144Z",
+ "updated_at": "2025-12-10T07:51:10.144Z"
+ }
+},
+{
+ "model": "igny8_core_auth.sector",
+ "pk": 61,
+ "fields": {
+ "is_deleted": false,
+ "deleted_at": null,
+ "restore_until": null,
+ "delete_reason": null,
+ "deleted_by": null,
+ "account": 90,
+ "site": 21,
+ "industry_sector": 101,
+ "name": "Physiotherapy & Rehabilitation",
+ "slug": "physiotherapy-rehabilitation",
+ "description": "",
+ "is_active": true,
+ "status": "active",
+ "created_at": "2025-12-17T05:40:13.377Z",
+ "updated_at": "2026-01-01T04:22:28.190Z"
+ }
+},
+{
+ "model": "igny8_core_auth.sector",
+ "pk": 62,
+ "fields": {
+ "is_deleted": false,
+ "deleted_at": null,
+ "restore_until": null,
+ "delete_reason": null,
+ "deleted_by": null,
+ "account": 90,
+ "site": 21,
+ "industry_sector": 103,
+ "name": "Mental Health & Therapy",
+ "slug": "mental-health-therapy",
+ "description": "",
+ "is_active": true,
+ "status": "active",
+ "created_at": "2026-01-01T04:22:28.197Z",
+ "updated_at": "2026-01-01T04:22:28.197Z"
+ }
+}
+]
diff --git a/backups/config/20260113/seed_keywords.json b/backups/config/20260113/seed_keywords.json
new file mode 100644
index 00000000..5557aaea
--- /dev/null
+++ b/backups/config/20260113/seed_keywords.json
@@ -0,0 +1,1592 @@
+[
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 146,
+ "fields": {
+ "keyword": "back and neck massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 900,
+ "difficulty": 6,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.805Z",
+ "updated_at": "2025-12-01T03:36:50.805Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 181,
+ "fields": {
+ "keyword": "back and shoulder massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 31,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.939Z",
+ "updated_at": "2025-12-01T03:36:50.939Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 178,
+ "fields": {
+ "keyword": "back hook massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.929Z",
+ "updated_at": "2025-12-01T03:36:50.929Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 140,
+ "fields": {
+ "keyword": "back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 23000,
+ "difficulty": 1,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.784Z",
+ "updated_at": "2025-12-01T03:36:50.784Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 142,
+ "fields": {
+ "keyword": "back massager for chair",
+ "industry": 25,
+ "sector": 101,
+ "volume": 1700,
+ "difficulty": 3,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.792Z",
+ "updated_at": "2025-12-01T03:36:50.792Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 154,
+ "fields": {
+ "keyword": "back massager gun",
+ "industry": 25,
+ "sector": 101,
+ "volume": 450,
+ "difficulty": 22,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.836Z",
+ "updated_at": "2025-12-01T03:36:50.836Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 167,
+ "fields": {
+ "keyword": "back massager handheld",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 5,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.893Z",
+ "updated_at": "2025-12-01T03:36:50.893Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 157,
+ "fields": {
+ "keyword": "back massager nearby",
+ "industry": 25,
+ "sector": 101,
+ "volume": 400,
+ "difficulty": 1,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.847Z",
+ "updated_at": "2025-12-01T03:36:50.847Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 159,
+ "fields": {
+ "keyword": "back massager near me",
+ "industry": 25,
+ "sector": 101,
+ "volume": 400,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.855Z",
+ "updated_at": "2025-12-01T03:36:50.855Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 180,
+ "fields": {
+ "keyword": "back massager stick",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 3,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.936Z",
+ "updated_at": "2025-12-01T03:36:50.936Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 160,
+ "fields": {
+ "keyword": "back massager walmart",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.861Z",
+ "updated_at": "2025-12-01T03:36:50.861Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 150,
+ "fields": {
+ "keyword": "back massager with heat",
+ "industry": 25,
+ "sector": 101,
+ "volume": 600,
+ "difficulty": 5,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.822Z",
+ "updated_at": "2025-12-01T03:36:50.822Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 172,
+ "fields": {
+ "keyword": "back roller massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 1,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.909Z",
+ "updated_at": "2025-12-01T03:36:50.909Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 188,
+ "fields": {
+ "keyword": "back vibrating massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 150,
+ "difficulty": 12,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.963Z",
+ "updated_at": "2025-12-01T03:36:50.963Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 37,
+ "fields": {
+ "keyword": "bathroom renovation",
+ "industry": 21,
+ "sector": 60,
+ "volume": 650,
+ "difficulty": 44,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.496Z",
+ "updated_at": "2025-11-09T00:05:33.496Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 176,
+ "fields": {
+ "keyword": "best back and neck massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 22,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.922Z",
+ "updated_at": "2025-12-01T03:36:50.922Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 141,
+ "fields": {
+ "keyword": "best back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 2200,
+ "difficulty": 6,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.788Z",
+ "updated_at": "2025-12-01T03:36:50.788Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 153,
+ "fields": {
+ "keyword": "best back massager for chair",
+ "industry": 25,
+ "sector": 101,
+ "volume": 600,
+ "difficulty": 2,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.833Z",
+ "updated_at": "2025-12-01T03:36:50.833Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 173,
+ "fields": {
+ "keyword": "best back massager for knots",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 16,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.913Z",
+ "updated_at": "2025-12-01T03:36:50.913Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 174,
+ "fields": {
+ "keyword": "best back massager for lower back pain",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.916Z",
+ "updated_at": "2025-12-01T03:36:50.916Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 168,
+ "fields": {
+ "keyword": "best handheld back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 5,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.897Z",
+ "updated_at": "2025-12-01T03:36:50.897Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 161,
+ "fields": {
+ "keyword": "best lower back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 10,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.865Z",
+ "updated_at": "2025-12-01T03:36:50.865Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 169,
+ "fields": {
+ "keyword": "best massager for back",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 11,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.900Z",
+ "updated_at": "2025-12-01T03:36:50.900Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 166,
+ "fields": {
+ "keyword": "best neck and back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 14,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.890Z",
+ "updated_at": "2025-12-01T03:36:50.890Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 165,
+ "fields": {
+ "keyword": "car back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.885Z",
+ "updated_at": "2025-12-01T03:36:50.885Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 155,
+ "fields": {
+ "keyword": "chair back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 450,
+ "difficulty": 3,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.840Z",
+ "updated_at": "2025-12-01T03:36:50.840Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 17,
+ "fields": {
+ "keyword": "color scheme ideas",
+ "industry": 21,
+ "sector": 62,
+ "volume": 650,
+ "difficulty": 44,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.472Z",
+ "updated_at": "2025-11-09T00:05:33.472Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 45,
+ "fields": {
+ "keyword": "composting guide",
+ "industry": 21,
+ "sector": 59,
+ "volume": 750,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.505Z",
+ "updated_at": "2025-11-09T00:05:33.505Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 8,
+ "fields": {
+ "keyword": "curtain design tips",
+ "industry": 21,
+ "sector": 63,
+ "volume": 600,
+ "difficulty": 36,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.461Z",
+ "updated_at": "2025-11-09T00:05:33.461Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 190,
+ "fields": {
+ "keyword": "dasdasd",
+ "industry": 25,
+ "sector": 101,
+ "volume": 3234,
+ "difficulty": 23,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-12T20:20:23.248Z",
+ "updated_at": "2025-12-12T20:20:23.248Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 27,
+ "fields": {
+ "keyword": "deck building guide",
+ "industry": 21,
+ "sector": 61,
+ "volume": 650,
+ "difficulty": 44,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.483Z",
+ "updated_at": "2025-11-09T00:05:33.483Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 14,
+ "fields": {
+ "keyword": "decoration trends",
+ "industry": 21,
+ "sector": 62,
+ "volume": 800,
+ "difficulty": 68,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.468Z",
+ "updated_at": "2025-11-09T00:05:33.468Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 4,
+ "fields": {
+ "keyword": "decorative mirrors",
+ "industry": 21,
+ "sector": 63,
+ "volume": 800,
+ "difficulty": 68,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.456Z",
+ "updated_at": "2025-11-09T00:05:33.456Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 170,
+ "fields": {
+ "keyword": "deep tissue back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 3,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.903Z",
+ "updated_at": "2025-12-01T03:36:50.903Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 39,
+ "fields": {
+ "keyword": "diy home projects",
+ "industry": 21,
+ "sector": 60,
+ "volume": 550,
+ "difficulty": 28,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.498Z",
+ "updated_at": "2025-11-09T00:05:33.498Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 156,
+ "fields": {
+ "keyword": "electric back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 400,
+ "difficulty": 9,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.843Z",
+ "updated_at": "2025-12-01T03:36:50.843Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 177,
+ "fields": {
+ "keyword": "electric massager for back pain",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 13,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.925Z",
+ "updated_at": "2025-12-01T03:36:50.925Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 36,
+ "fields": {
+ "keyword": "flooring installation",
+ "industry": 21,
+ "sector": 60,
+ "volume": 700,
+ "difficulty": 52,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.495Z",
+ "updated_at": "2025-11-09T00:05:33.495Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 48,
+ "fields": {
+ "keyword": "flower garden design",
+ "industry": 21,
+ "sector": 59,
+ "volume": 600,
+ "difficulty": 36,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.508Z",
+ "updated_at": "2025-11-09T00:05:33.508Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 18,
+ "fields": {
+ "keyword": "furniture arrangement",
+ "industry": 21,
+ "sector": 62,
+ "volume": 600,
+ "difficulty": 36,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.473Z",
+ "updated_at": "2025-11-09T00:05:33.473Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 21,
+ "fields": {
+ "keyword": "garden edging ideas",
+ "industry": 21,
+ "sector": 61,
+ "volume": 950,
+ "difficulty": 92,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.476Z",
+ "updated_at": "2025-11-09T00:05:33.476Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 42,
+ "fields": {
+ "keyword": "garden irrigation systems",
+ "industry": 21,
+ "sector": 59,
+ "volume": 900,
+ "difficulty": 84,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.502Z",
+ "updated_at": "2025-11-09T00:05:33.502Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 26,
+ "fields": {
+ "keyword": "garden pathways",
+ "industry": 21,
+ "sector": 61,
+ "volume": 700,
+ "difficulty": 52,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.482Z",
+ "updated_at": "2025-11-09T00:05:33.482Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 44,
+ "fields": {
+ "keyword": "garden pest control",
+ "industry": 21,
+ "sector": 59,
+ "volume": 800,
+ "difficulty": 68,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.504Z",
+ "updated_at": "2025-11-09T00:05:33.504Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 47,
+ "fields": {
+ "keyword": "garden tools",
+ "industry": 21,
+ "sector": 59,
+ "volume": 650,
+ "difficulty": 44,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.507Z",
+ "updated_at": "2025-11-09T00:05:33.507Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 163,
+ "fields": {
+ "keyword": "hand held back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 7,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.876Z",
+ "updated_at": "2025-12-01T03:36:50.876Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 147,
+ "fields": {
+ "keyword": "handheld back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 900,
+ "difficulty": 7,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.810Z",
+ "updated_at": "2025-12-01T03:36:50.810Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 152,
+ "fields": {
+ "keyword": "heated back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 600,
+ "difficulty": 7,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.830Z",
+ "updated_at": "2025-12-01T03:36:50.830Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 43,
+ "fields": {
+ "keyword": "herb garden ideas",
+ "industry": 21,
+ "sector": 59,
+ "volume": 850,
+ "difficulty": 76,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.503Z",
+ "updated_at": "2025-11-09T00:05:33.503Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 2,
+ "fields": {
+ "keyword": "home accent pieces",
+ "industry": 21,
+ "sector": 63,
+ "volume": 900,
+ "difficulty": 84,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.449Z",
+ "updated_at": "2025-11-09T00:05:33.449Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 10,
+ "fields": {
+ "keyword": "home decor accessories",
+ "industry": 21,
+ "sector": 63,
+ "volume": 500,
+ "difficulty": 20,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.463Z",
+ "updated_at": "2025-11-09T00:05:33.463Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 19,
+ "fields": {
+ "keyword": "home decor ideas",
+ "industry": 21,
+ "sector": 62,
+ "volume": 550,
+ "difficulty": 28,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.474Z",
+ "updated_at": "2025-11-09T00:05:33.474Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 148,
+ "fields": {
+ "keyword": "homedics back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 800,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.813Z",
+ "updated_at": "2025-12-01T03:36:50.813Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 5,
+ "fields": {
+ "keyword": "home fragrance tips",
+ "industry": 21,
+ "sector": 63,
+ "volume": 750,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.457Z",
+ "updated_at": "2025-11-09T00:05:33.457Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 32,
+ "fields": {
+ "keyword": "home maintenance checklist",
+ "industry": 21,
+ "sector": 60,
+ "volume": 900,
+ "difficulty": 84,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.490Z",
+ "updated_at": "2025-11-09T00:05:33.490Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 11,
+ "fields": {
+ "keyword": "home organization tips",
+ "industry": 21,
+ "sector": 62,
+ "volume": 950,
+ "difficulty": 92,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.464Z",
+ "updated_at": "2025-11-09T00:05:33.464Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 40,
+ "fields": {
+ "keyword": "home renovation ideas",
+ "industry": 21,
+ "sector": 60,
+ "volume": 500,
+ "difficulty": 20,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.499Z",
+ "updated_at": "2025-11-09T00:05:33.499Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 34,
+ "fields": {
+ "keyword": "home repair guide",
+ "industry": 21,
+ "sector": 60,
+ "volume": 800,
+ "difficulty": 68,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.492Z",
+ "updated_at": "2025-11-09T00:05:33.492Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 15,
+ "fields": {
+ "keyword": "home staging tips",
+ "industry": 21,
+ "sector": 62,
+ "volume": 750,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.469Z",
+ "updated_at": "2025-11-09T00:05:33.469Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 171,
+ "fields": {
+ "keyword": "hyperice back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 22,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.906Z",
+ "updated_at": "2025-12-01T03:36:50.906Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 20,
+ "fields": {
+ "keyword": "interior design styles",
+ "industry": 21,
+ "sector": 62,
+ "volume": 500,
+ "difficulty": 20,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.475Z",
+ "updated_at": "2025-11-09T00:05:33.475Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 31,
+ "fields": {
+ "keyword": "interior design trends",
+ "industry": 21,
+ "sector": 60,
+ "volume": 950,
+ "difficulty": 92,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.489Z",
+ "updated_at": "2025-11-09T00:05:33.489Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 12,
+ "fields": {
+ "keyword": "interior lighting design",
+ "industry": 21,
+ "sector": 62,
+ "volume": 900,
+ "difficulty": 84,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.466Z",
+ "updated_at": "2025-11-09T00:05:33.466Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 186,
+ "fields": {
+ "keyword": "is it safe to use a vibrating back massager during pregnancy",
+ "industry": 25,
+ "sector": 101,
+ "volume": 150,
+ "difficulty": 7,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.956Z",
+ "updated_at": "2025-12-01T03:36:50.956Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 136,
+ "fields": {
+ "keyword": "keyword 1",
+ "industry": 1,
+ "sector": 1,
+ "volume": 10000,
+ "difficulty": 45,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-30T05:01:02.843Z",
+ "updated_at": "2025-11-30T05:01:02.843Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 137,
+ "fields": {
+ "keyword": "keywrod 2",
+ "industry": 25,
+ "sector": 106,
+ "volume": 5000,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-30T05:01:02.862Z",
+ "updated_at": "2025-11-30T05:01:02.862Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 38,
+ "fields": {
+ "keyword": "kitchen remodeling",
+ "industry": 21,
+ "sector": 60,
+ "volume": 600,
+ "difficulty": 36,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.497Z",
+ "updated_at": "2025-11-09T00:05:33.497Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 30,
+ "fields": {
+ "keyword": "landscape design ideas",
+ "industry": 21,
+ "sector": 61,
+ "volume": 500,
+ "difficulty": 20,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.487Z",
+ "updated_at": "2025-11-09T00:05:33.487Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 29,
+ "fields": {
+ "keyword": "lawn care tips",
+ "industry": 21,
+ "sector": 61,
+ "volume": 550,
+ "difficulty": 28,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.486Z",
+ "updated_at": "2025-11-09T00:05:33.486Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 24,
+ "fields": {
+ "keyword": "lawn mowing tips",
+ "industry": 21,
+ "sector": 61,
+ "volume": 800,
+ "difficulty": 68,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.479Z",
+ "updated_at": "2025-11-09T00:05:33.479Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 144,
+ "fields": {
+ "keyword": "lower back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 1200,
+ "difficulty": 9,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.799Z",
+ "updated_at": "2025-12-01T03:36:50.799Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 184,
+ "fields": {
+ "keyword": "massager back",
+ "industry": 25,
+ "sector": 101,
+ "volume": 150,
+ "difficulty": 11,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.949Z",
+ "updated_at": "2025-12-01T03:36:50.949Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 143,
+ "fields": {
+ "keyword": "massager for back",
+ "industry": 25,
+ "sector": 101,
+ "volume": 1400,
+ "difficulty": 4,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.795Z",
+ "updated_at": "2025-12-01T03:36:50.795Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 182,
+ "fields": {
+ "keyword": "massager for back pain",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 15,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.942Z",
+ "updated_at": "2025-12-01T03:36:50.942Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 139,
+ "fields": {
+ "keyword": "medical software",
+ "industry": 25,
+ "sector": 101,
+ "volume": 5000,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.778Z",
+ "updated_at": "2025-12-01T03:36:50.778Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 145,
+ "fields": {
+ "keyword": "neck and back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 1000,
+ "difficulty": 10,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.802Z",
+ "updated_at": "2025-12-01T03:36:50.802Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 50,
+ "fields": {
+ "keyword": "organic gardening",
+ "industry": 21,
+ "sector": 59,
+ "volume": 500,
+ "difficulty": 20,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.510Z",
+ "updated_at": "2025-11-09T00:05:33.510Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 22,
+ "fields": {
+ "keyword": "outdoor kitchen design",
+ "industry": 21,
+ "sector": 61,
+ "volume": 900,
+ "difficulty": 84,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.477Z",
+ "updated_at": "2025-11-09T00:05:33.477Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 25,
+ "fields": {
+ "keyword": "outdoor lighting ideas",
+ "industry": 21,
+ "sector": 61,
+ "volume": 750,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.481Z",
+ "updated_at": "2025-11-09T00:05:33.481Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 28,
+ "fields": {
+ "keyword": "outdoor patio design",
+ "industry": 21,
+ "sector": 61,
+ "volume": 600,
+ "difficulty": 36,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.485Z",
+ "updated_at": "2025-11-09T00:05:33.485Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 35,
+ "fields": {
+ "keyword": "painting tips",
+ "industry": 21,
+ "sector": 60,
+ "volume": 750,
+ "difficulty": 60,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.494Z",
+ "updated_at": "2025-11-09T00:05:33.494Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 7,
+ "fields": {
+ "keyword": "pillow arrangement",
+ "industry": 21,
+ "sector": 63,
+ "volume": 650,
+ "difficulty": 44,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.460Z",
+ "updated_at": "2025-11-09T00:05:33.460Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 46,
+ "fields": {
+ "keyword": "plant care tips",
+ "industry": 21,
+ "sector": 59,
+ "volume": 700,
+ "difficulty": 52,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.506Z",
+ "updated_at": "2025-11-09T00:05:33.506Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 164,
+ "fields": {
+ "keyword": "portable back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 1,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.880Z",
+ "updated_at": "2025-12-01T03:36:50.880Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 33,
+ "fields": {
+ "keyword": "power tools review",
+ "industry": 21,
+ "sector": 60,
+ "volume": 850,
+ "difficulty": 76,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.491Z",
+ "updated_at": "2025-11-09T00:05:33.491Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 138,
+ "fields": {
+ "keyword": "python programming",
+ "industry": 25,
+ "sector": 101,
+ "volume": 10000,
+ "difficulty": 45,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.765Z",
+ "updated_at": "2025-12-01T03:36:50.765Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 185,
+ "fields": {
+ "keyword": "renpho back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 150,
+ "difficulty": 1,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.952Z",
+ "updated_at": "2025-12-01T03:36:50.952Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 13,
+ "fields": {
+ "keyword": "room makeover ideas",
+ "industry": 21,
+ "sector": 62,
+ "volume": 850,
+ "difficulty": 76,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.467Z",
+ "updated_at": "2025-11-09T00:05:33.467Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 3,
+ "fields": {
+ "keyword": "rug selection guide",
+ "industry": 21,
+ "sector": 63,
+ "volume": 850,
+ "difficulty": 76,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.455Z",
+ "updated_at": "2025-11-09T00:05:33.455Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 1,
+ "fields": {
+ "keyword": "seasonal home decor",
+ "industry": 21,
+ "sector": 63,
+ "volume": 950,
+ "difficulty": 92,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.446Z",
+ "updated_at": "2025-11-09T00:05:33.446Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 41,
+ "fields": {
+ "keyword": "seasonal planting guide",
+ "industry": 21,
+ "sector": 59,
+ "volume": 950,
+ "difficulty": 92,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.501Z",
+ "updated_at": "2025-11-09T00:05:33.501Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 179,
+ "fields": {
+ "keyword": "self back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 22,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.932Z",
+ "updated_at": "2025-12-01T03:36:50.932Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 183,
+ "fields": {
+ "keyword": "shiatsu back and neck massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 2,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.946Z",
+ "updated_at": "2025-12-01T03:36:50.946Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 149,
+ "fields": {
+ "keyword": "shiatsu back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 700,
+ "difficulty": 9,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.818Z",
+ "updated_at": "2025-12-01T03:36:50.818Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 151,
+ "fields": {
+ "keyword": "shiatsu neck and back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 600,
+ "difficulty": 4,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.826Z",
+ "updated_at": "2025-12-01T03:36:50.826Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 189,
+ "fields": {
+ "keyword": "shop back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 250,
+ "difficulty": 33,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.966Z",
+ "updated_at": "2025-12-01T03:36:50.966Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 16,
+ "fields": {
+ "keyword": "small space design",
+ "industry": 21,
+ "sector": 62,
+ "volume": 700,
+ "difficulty": 52,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.470Z",
+ "updated_at": "2025-11-09T00:05:33.470Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 191,
+ "fields": {
+ "keyword": "test 1",
+ "industry": 25,
+ "sector": 101,
+ "volume": 34422,
+ "difficulty": 59,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-12T20:53:31.059Z",
+ "updated_at": "2025-12-12T20:53:31.059Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 23,
+ "fields": {
+ "keyword": "tree planting guide",
+ "industry": 21,
+ "sector": 61,
+ "volume": 850,
+ "difficulty": 76,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.478Z",
+ "updated_at": "2025-11-09T00:05:33.478Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 175,
+ "fields": {
+ "keyword": "upper back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 200,
+ "difficulty": 2,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.919Z",
+ "updated_at": "2025-12-01T03:36:50.919Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 6,
+ "fields": {
+ "keyword": "vase decoration ideas",
+ "industry": 21,
+ "sector": 63,
+ "volume": 700,
+ "difficulty": 52,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.459Z",
+ "updated_at": "2025-11-09T00:05:33.459Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 49,
+ "fields": {
+ "keyword": "vegetable gardening",
+ "industry": 21,
+ "sector": 59,
+ "volume": 550,
+ "difficulty": 28,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.509Z",
+ "updated_at": "2025-11-09T00:05:33.509Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 158,
+ "fields": {
+ "keyword": "venom back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 400,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.850Z",
+ "updated_at": "2025-12-01T03:36:50.850Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 162,
+ "fields": {
+ "keyword": "vibrating back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 300,
+ "difficulty": 10,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.870Z",
+ "updated_at": "2025-12-01T03:36:50.870Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 187,
+ "fields": {
+ "keyword": "wahl back massager",
+ "industry": 25,
+ "sector": 101,
+ "volume": 150,
+ "difficulty": 0,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-12-01T03:36:50.960Z",
+ "updated_at": "2025-12-01T03:36:50.960Z"
+ }
+},
+{
+ "model": "igny8_core_auth.seedkeyword",
+ "pk": 9,
+ "fields": {
+ "keyword": "wall art ideas",
+ "industry": 21,
+ "sector": 63,
+ "volume": 550,
+ "difficulty": 28,
+ "country": "US",
+ "is_active": true,
+ "created_at": "2025-11-09T00:05:33.462Z",
+ "updated_at": "2025-11-09T00:05:33.462Z"
+ }
+}
+]