- Furter refinement of the API, adding functionality for refreshing documents and returning Token expiration time when retrieving token
- Implementation of a first version of a Wordpress plugin - Adding api service to nginx.conf
This commit is contained in:
@@ -282,15 +282,22 @@ def edit_document_version(version_id, user_context):
|
|||||||
return None, str(e)
|
return None, str(e)
|
||||||
|
|
||||||
|
|
||||||
def refresh_document(doc_id):
|
def refresh_document_with_info(doc_id, api_input):
|
||||||
doc = Document.query.get_or_404(doc_id)
|
doc = Document.query.get_or_404(doc_id)
|
||||||
doc_vers = DocumentVersion.query.filter_by(doc_id=doc_id).order_by(desc(DocumentVersion.id)).first()
|
old_doc_vers = DocumentVersion.query.filter_by(doc_id=doc_id).order_by(desc(DocumentVersion.id)).first()
|
||||||
|
|
||||||
if not doc_vers.url:
|
if not old_doc_vers.url:
|
||||||
return None, f"This document {doc_id} has no URL. Only documents with a URL can be refreshed."
|
return None, "This document has no URL. Only documents with a URL can be refreshed."
|
||||||
|
|
||||||
new_doc_vers = create_version_for_document(doc, doc_vers.url, doc_vers.language, doc_vers.user_context,
|
new_doc_vers = create_version_for_document(
|
||||||
doc_vers.user_metadata)
|
doc,
|
||||||
|
old_doc_vers.url,
|
||||||
|
api_input.get('language', old_doc_vers.language),
|
||||||
|
api_input.get('user_context', old_doc_vers.user_context),
|
||||||
|
api_input.get('user_metadata', old_doc_vers.user_metadata)
|
||||||
|
)
|
||||||
|
|
||||||
|
set_logging_information(new_doc_vers, dt.now(tz.utc))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.add(new_doc_vers)
|
db.session.add(new_doc_vers)
|
||||||
@@ -299,14 +306,11 @@ def refresh_document(doc_id):
|
|||||||
db.session.rollback()
|
db.session.rollback()
|
||||||
return None, str(e)
|
return None, str(e)
|
||||||
|
|
||||||
response = requests.head(doc_vers.url, allow_redirects=True)
|
response = requests.head(old_doc_vers.url, allow_redirects=True)
|
||||||
content_type = response.headers.get('Content-Type', '').split(';')[0]
|
content_type = response.headers.get('Content-Type', '').split(';')[0]
|
||||||
|
|
||||||
# Determine file extension based on Content-Type
|
|
||||||
extension = get_extension_from_content_type(content_type)
|
extension = get_extension_from_content_type(content_type)
|
||||||
|
|
||||||
# Download the content
|
response = requests.get(old_doc_vers.url)
|
||||||
response = requests.get(doc_vers.url)
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
file_content = response.content
|
file_content = response.content
|
||||||
|
|
||||||
@@ -318,3 +322,17 @@ def refresh_document(doc_id):
|
|||||||
])
|
])
|
||||||
|
|
||||||
return new_doc_vers, task.id
|
return new_doc_vers, task.id
|
||||||
|
|
||||||
|
|
||||||
|
# Update the existing refresh_document function to use the new refresh_document_with_info
|
||||||
|
def refresh_document(doc_id):
|
||||||
|
doc = Document.query.get_or_404(doc_id)
|
||||||
|
old_doc_vers = DocumentVersion.query.filter_by(doc_id=doc_id).order_by(desc(DocumentVersion.id)).first()
|
||||||
|
|
||||||
|
api_input = {
|
||||||
|
'language': old_doc_vers.language,
|
||||||
|
'user_context': old_doc_vers.user_context,
|
||||||
|
'user_metadata': old_doc_vers.user_metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
return refresh_document_with_info(doc_id, api_input)
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ class Config(object):
|
|||||||
|
|
||||||
# JWT settings
|
# JWT settings
|
||||||
JWT_SECRET_KEY = environ.get('JWT_SECRET_KEY')
|
JWT_SECRET_KEY = environ.get('JWT_SECRET_KEY')
|
||||||
|
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1) # Set token expiry to 1 hour
|
||||||
|
|
||||||
# API Encryption
|
# API Encryption
|
||||||
API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY')
|
API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY')
|
||||||
|
|||||||
@@ -50,9 +50,26 @@ def create_app(config_file=None):
|
|||||||
@app.before_request
|
@app.before_request
|
||||||
def before_request():
|
def before_request():
|
||||||
app.logger.debug(f'Before request: {request.method} {request.path}')
|
app.logger.debug(f'Before request: {request.method} {request.path}')
|
||||||
|
app.logger.debug(f'Request URL: {request.url}')
|
||||||
|
app.logger.debug(f'Request headers: {dict(request.headers)}')
|
||||||
|
|
||||||
|
# Log request arguments
|
||||||
|
app.logger.debug(f'Request args: {request.args}')
|
||||||
|
|
||||||
|
# Log form data if it's a POST request
|
||||||
|
if request.method == 'POST':
|
||||||
|
app.logger.debug(f'Form data: {request.form}')
|
||||||
|
|
||||||
|
# Log JSON data if the content type is application/json
|
||||||
|
if request.is_json:
|
||||||
|
app.logger.debug(f'JSON data: {request.json}')
|
||||||
|
|
||||||
|
# Log raw data for other content types
|
||||||
|
if request.data:
|
||||||
|
app.logger.debug(f'Raw data: {request.data}')
|
||||||
|
|
||||||
# Check if this is a request to the token endpoint
|
# Check if this is a request to the token endpoint
|
||||||
if request.path == '/api/v1/token' and request.method == 'POST':
|
if request.path == '/api/v1/auth/token' and request.method == 'POST':
|
||||||
app.logger.debug('Token request detected, skipping JWT verification')
|
app.logger.debug('Token request detected, skipping JWT verification')
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -73,8 +90,6 @@ def create_app(config_file=None):
|
|||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
return app
|
|
||||||
|
|
||||||
|
|
||||||
def register_extensions(app):
|
def register_extensions(app):
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
@@ -86,4 +101,4 @@ def register_extensions(app):
|
|||||||
|
|
||||||
def register_namespaces(app):
|
def register_namespaces(app):
|
||||||
api_rest.add_namespace(document_ns, path='/api/v1/documents')
|
api_rest.add_namespace(document_ns, path='/api/v1/documents')
|
||||||
api_rest.add_namespace(auth_ns, path='/api/v1')
|
api_rest.add_namespace(auth_ns, path='/api/v1/auth')
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from flask_restx import Namespace, Resource, fields
|
from flask_restx import Namespace, Resource, fields
|
||||||
from flask_jwt_extended import create_access_token
|
from flask_jwt_extended import create_access_token
|
||||||
from common.models.user import Tenant
|
from common.models.user import Tenant
|
||||||
@@ -12,7 +14,8 @@ token_model = auth_ns.model('Token', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
token_response = auth_ns.model('TokenResponse', {
|
token_response = auth_ns.model('TokenResponse', {
|
||||||
'access_token': fields.String(description='JWT access token')
|
'access_token': fields.String(description='JWT access token'),
|
||||||
|
'expires_in': fields.Integer(description='Token expiration time in seconds')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -56,11 +59,17 @@ class Token(Resource):
|
|||||||
current_app.logger.error(f"Invalid API key for tenant: {tenant_id}")
|
current_app.logger.error(f"Invalid API key for tenant: {tenant_id}")
|
||||||
return {'message': "Invalid API key"}, 401
|
return {'message': "Invalid API key"}, 401
|
||||||
|
|
||||||
|
# Get the JWT_ACCESS_TOKEN_EXPIRES setting from the app config
|
||||||
|
expires_delta = current_app.config.get('JWT_ACCESS_TOKEN_EXPIRES', timedelta(minutes=15))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
current_app.logger.debug(f"Creating access token for tenant: {tenant_id}")
|
current_app.logger.debug(f"Creating access token for tenant: {tenant_id}")
|
||||||
access_token = create_access_token(identity=tenant_id)
|
access_token = create_access_token(identity=tenant_id, expires_delta=expires_delta)
|
||||||
current_app.logger.debug("Access token created successfully")
|
current_app.logger.debug("Access token created successfully")
|
||||||
return {'access_token': access_token}, 200
|
return {
|
||||||
|
'access_token': access_token,
|
||||||
|
'expires_in': expires_delta.total_seconds()
|
||||||
|
}, 200
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"Error creating access token: {e}")
|
current_app.logger.error(f"Error creating access token: {e}")
|
||||||
return {'message': "Internal server error"}, 500
|
return {'message': "Internal server error"}, 500
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ from werkzeug.utils import secure_filename
|
|||||||
from common.utils.document_utils import (
|
from common.utils.document_utils import (
|
||||||
create_document_stack, process_url, start_embedding_task,
|
create_document_stack, process_url, start_embedding_task,
|
||||||
validate_file_type, EveAIInvalidLanguageException, EveAIDoubleURLException, EveAIUnsupportedFileType,
|
validate_file_type, EveAIInvalidLanguageException, EveAIDoubleURLException, EveAIUnsupportedFileType,
|
||||||
process_multiple_urls, get_documents_list, edit_document, refresh_document, edit_document_version
|
process_multiple_urls, get_documents_list, edit_document, refresh_document, edit_document_version,
|
||||||
|
refresh_document_with_info
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -238,3 +239,75 @@ class DocumentVersionResource(Resource):
|
|||||||
return {'message': f'Document Version {updated_version.id} updated successfully'}, 200
|
return {'message': f'Document Version {updated_version.id} updated successfully'}, 200
|
||||||
else:
|
else:
|
||||||
return {'message': f'Error updating document version: {error}'}, 400
|
return {'message': f'Error updating document version: {error}'}, 400
|
||||||
|
|
||||||
|
|
||||||
|
# Define the model for the request body of refresh_with_info
|
||||||
|
refresh_document_model = document_ns.model('RefreshDocument', {
|
||||||
|
'name': fields.String(required=False, description='New name for the document'),
|
||||||
|
'language': fields.String(required=False, description='Language of the document'),
|
||||||
|
'user_context': fields.String(required=False, description='User context for the document'),
|
||||||
|
'user_metadata': fields.Raw(required=False, description='User metadata for the document')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@document_ns.route('/<int:document_id>/refresh')
|
||||||
|
class RefreshDocument(Resource):
|
||||||
|
@jwt_required()
|
||||||
|
@document_ns.response(200, 'Document refreshed successfully')
|
||||||
|
@document_ns.response(404, 'Document not found')
|
||||||
|
def post(self, document_id):
|
||||||
|
"""
|
||||||
|
Refresh a document without additional information
|
||||||
|
"""
|
||||||
|
tenant_id = get_jwt_identity()
|
||||||
|
current_app.logger.info(f'Refreshing document {document_id} for tenant {tenant_id}')
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_version, result = refresh_document(document_id)
|
||||||
|
|
||||||
|
if new_version:
|
||||||
|
return {
|
||||||
|
'message': f'Document refreshed successfully. New version: {new_version.id}. Task ID: {result}',
|
||||||
|
'document_id': document_id,
|
||||||
|
'document_version_id': new_version.id,
|
||||||
|
'task_id': result
|
||||||
|
}, 200
|
||||||
|
else:
|
||||||
|
return {'message': f'Error refreshing document: {result}'}, 400
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Error refreshing document: {str(e)}')
|
||||||
|
return {'message': 'Internal server error'}, 500
|
||||||
|
|
||||||
|
|
||||||
|
@document_ns.route('/<int:document_id>/refresh_with_info')
|
||||||
|
class RefreshDocumentWithInfo(Resource):
|
||||||
|
@jwt_required()
|
||||||
|
@document_ns.expect(refresh_document_model)
|
||||||
|
@document_ns.response(200, 'Document refreshed successfully')
|
||||||
|
@document_ns.response(400, 'Validation Error')
|
||||||
|
@document_ns.response(404, 'Document not found')
|
||||||
|
def post(self, document_id):
|
||||||
|
"""
|
||||||
|
Refresh a document with new information
|
||||||
|
"""
|
||||||
|
tenant_id = get_jwt_identity()
|
||||||
|
current_app.logger.info(f'Refreshing document {document_id} with info for tenant {tenant_id}')
|
||||||
|
|
||||||
|
try:
|
||||||
|
api_input = request.json
|
||||||
|
new_version, result = refresh_document_with_info(document_id, api_input)
|
||||||
|
|
||||||
|
if new_version:
|
||||||
|
return {
|
||||||
|
'message': f'Document refreshed successfully with new info. New version: {new_version.id}. Task ID: {result}',
|
||||||
|
'document_id': document_id,
|
||||||
|
'document_version_id': new_version.id,
|
||||||
|
'task_id': result
|
||||||
|
}, 200
|
||||||
|
else:
|
||||||
|
return {'message': f'Error refreshing document with info: {result}'}, 400
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error(f'Error refreshing document with info: {str(e)}')
|
||||||
|
return {'message': 'Internal server error'}, 500
|
||||||
|
|||||||
Binary file not shown.
83
integrations/Wordpress/eveai_sync/README.md
Normal file
83
integrations/Wordpress/eveai_sync/README.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# EveAI Sync WordPress Plugin
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
EveAI Sync is a WordPress plugin that synchronizes your WordPress content (posts and pages) with the EveAI platform. It allows for seamless integration between your WordPress site and EveAI, ensuring that your content is always up-to-date on both platforms.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Automatic synchronization of posts and pages with EveAI
|
||||||
|
- Support for excluding specific categories or individual posts/pages from syncing
|
||||||
|
- Bulk synchronization of existing content
|
||||||
|
- Custom metadata synchronization
|
||||||
|
- Easy-to-use admin interface for configuration
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Download the plugin zip file.
|
||||||
|
2. Log in to your WordPress admin panel.
|
||||||
|
3. Go to Plugins > Add New.
|
||||||
|
4. Click on the "Upload Plugin" button.
|
||||||
|
5. Select the downloaded zip file and click "Install Now".
|
||||||
|
6. After installation, click "Activate Plugin".
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
1. Go to Settings > EveAI Sync in your WordPress admin panel.
|
||||||
|
2. Enter your EveAI API URL, Tenant ID, and API Key.
|
||||||
|
3. Configure any additional settings as needed.
|
||||||
|
4. Click "Save Changes".
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
- New posts and pages will automatically sync to EveAI when published.
|
||||||
|
- Existing content can be synced using the "Bulk Sync" option in the settings.
|
||||||
|
- To exclude a post or page from syncing, use the "Exclude from EveAI sync" checkbox in the post editor.
|
||||||
|
- Categories can be excluded from syncing in the plugin settings.
|
||||||
|
|
||||||
|
## Action Scheduler Dependency
|
||||||
|
|
||||||
|
This plugin uses Action Scheduler for efficient background processing of synchronization tasks. Action Scheduler is typically included with WooCommerce, but the plugin can also function without it.
|
||||||
|
|
||||||
|
### With Action Scheduler
|
||||||
|
|
||||||
|
If Action Scheduler is available (either through WooCommerce or included with this plugin), EveAI Sync will use it for more reliable and efficient scheduling of synchronization tasks.
|
||||||
|
|
||||||
|
### Without Action Scheduler
|
||||||
|
|
||||||
|
If Action Scheduler is not available, the plugin will automatically fall back to using WordPress cron for scheduling tasks. This fallback ensures that the plugin remains functional, although with potentially less precise timing for background tasks.
|
||||||
|
|
||||||
|
No additional configuration is needed; the plugin will automatically detect the presence or absence of Action Scheduler and adjust its behavior accordingly.
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
### 1.0.x - Bugfixing Releases
|
||||||
|
|
||||||
|
### 1.0.0 - Initial Release
|
||||||
|
|
||||||
|
|
||||||
|
## Frequently Asked Questions
|
||||||
|
|
||||||
|
**Q: How often does the plugin sync content?**
|
||||||
|
A: The plugin syncs content immediately when a post or page is published or updated. For bulk syncs or when Action Scheduler is not available, the timing may vary based on WordPress cron execution.
|
||||||
|
|
||||||
|
**Q: Can I sync only certain types of content?**
|
||||||
|
A: By default, the plugin syncs all posts and pages. You can exclude specific categories or individual posts/pages from syncing.
|
||||||
|
|
||||||
|
**Q: What happens if the sync fails?**
|
||||||
|
A: The plugin will log any sync failures and attempt to retry. You can view sync status in the plugin's admin interface.
|
||||||
|
|
||||||
|
**Q: Do I need to install Action Scheduler separately?**
|
||||||
|
A: No, the plugin will work with or without Action Scheduler. If you have WooCommerce installed, Action Scheduler will be available automatically.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For support, please open an issue on our GitHub repository or contact our support team at support@eveai.com.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We welcome contributions to the EveAI Sync plugin. Please feel free to submit pull requests or open issues on our GitHub repository.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This plugin is licensed under the GPL v2 or later.
|
||||||
70
integrations/Wordpress/eveai_sync/admin/css/eveai-admin.css
Normal file
70
integrations/Wordpress/eveai_sync/admin/css/eveai-admin.css
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
.eveai-admin-wrap {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-admin-header {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-admin-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
color: #23282d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-admin-content {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-form-group input[type="text"],
|
||||||
|
.eveai-form-group input[type="password"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-button {
|
||||||
|
background-color: #0085ba;
|
||||||
|
border-color: #0073aa #006799 #006799;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-button:hover {
|
||||||
|
background-color: #008ec2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-category-list {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-category-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eveai-category-item label {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
49
integrations/Wordpress/eveai_sync/admin/js/eveai_admin.js
Normal file
49
integrations/Wordpress/eveai_sync/admin/js/eveai_admin.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
jQuery(document).ready(function($) {
|
||||||
|
// Handle bulk sync button click
|
||||||
|
$('#eveai-bulk-sync').on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (confirm('Are you sure you want to start a bulk sync? This may take a while.')) {
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'eveai_bulk_sync',
|
||||||
|
nonce: eveai_admin.nonce
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
alert(response.data.message);
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('An error occurred. Please try again.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle category exclusion checkboxes
|
||||||
|
$('.eveai-category-exclude').on('change', function() {
|
||||||
|
var categoryId = $(this).val();
|
||||||
|
var isExcluded = $(this).is(':checked');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'eveai_toggle_category_exclusion',
|
||||||
|
category_id: categoryId,
|
||||||
|
is_excluded: isExcluded ? 1 : 0,
|
||||||
|
nonce: eveai_admin.nonce
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
console.log('Category exclusion updated');
|
||||||
|
} else {
|
||||||
|
alert('Failed to update category exclusion');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
alert('An error occurred. Please try again.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
64
integrations/Wordpress/eveai_sync/eveai_sync.php
Normal file
64
integrations/Wordpress/eveai_sync/eveai_sync.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: EveAI Sync
|
||||||
|
* Plugin URI: https://askeveai.com/
|
||||||
|
* Description: Synchronizes WordPress content with EveAI API.
|
||||||
|
* Version: 1.0.16
|
||||||
|
* Author: Josako, Pieter Laroy
|
||||||
|
* Author URI: https://askeveai.com/about/
|
||||||
|
* License: GPL v2 or later
|
||||||
|
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
* Text Domain: eveai-sync
|
||||||
|
* Domain Path: /languages
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define plugin constants
|
||||||
|
define('EVEAI_SYNC_VERSION', '1.0.0');
|
||||||
|
define('EVEAI_SYNC_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
|
define('EVEAI_SYNC_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||||
|
|
||||||
|
// Include the main plugin class
|
||||||
|
require_once EVEAI_SYNC_PLUGIN_DIR . 'includes/class-eveai-sync.php';
|
||||||
|
|
||||||
|
// Initialize the plugin
|
||||||
|
function eveai_sync_init() {
|
||||||
|
$plugin = new EveAI_Sync();
|
||||||
|
$plugin->init();
|
||||||
|
}
|
||||||
|
add_action('plugins_loaded', 'eveai_sync_init');
|
||||||
|
|
||||||
|
// Set up activation and deactivation hooks
|
||||||
|
register_activation_hook(__FILE__, 'eveai_sync_activation');
|
||||||
|
register_deactivation_hook(__FILE__, 'eveai_sync_deactivation');
|
||||||
|
|
||||||
|
function eveai_sync_activation() {
|
||||||
|
// Other activation tasks...
|
||||||
|
}
|
||||||
|
|
||||||
|
function eveai_sync_deactivation() {
|
||||||
|
// Other deactivation tasks...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up meta when a post is permanently deleted
|
||||||
|
function eveai_delete_post_meta($post_id) {
|
||||||
|
delete_post_meta($post_id, '_eveai_document_id');
|
||||||
|
delete_post_meta($post_id, '_eveai_document_version_id');
|
||||||
|
}
|
||||||
|
add_action('before_delete_post', 'eveai_delete_post_meta');
|
||||||
|
|
||||||
|
// Display sync info in post
|
||||||
|
function eveai_display_sync_info($post) {
|
||||||
|
$document_id = get_post_meta($post->ID, '_eveai_document_id', true);
|
||||||
|
$document_version_id = get_post_meta($post->ID, '_eveai_document_version_id', true);
|
||||||
|
|
||||||
|
echo '<div class="misc-pub-section">';
|
||||||
|
echo '<h4>EveAI Sync Info:</h4>';
|
||||||
|
echo 'Document ID: ' . ($document_id ? esc_html($document_id) : 'Not set') . '<br>';
|
||||||
|
echo 'Document Version ID: ' . ($document_version_id ? esc_html($document_version_id) : 'Not set');
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
add_action('post_submitbox_misc_actions', 'eveai_display_sync_info');
|
||||||
156
integrations/Wordpress/eveai_sync/includes/class-eveai-admin.php
Normal file
156
integrations/Wordpress/eveai_sync/includes/class-eveai-admin.php
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class EveAI_Admin {
|
||||||
|
private $api;
|
||||||
|
|
||||||
|
public function __construct($api) {
|
||||||
|
$this->api = $api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register_settings() {
|
||||||
|
register_setting('eveai_settings', 'eveai_api_url');
|
||||||
|
register_setting('eveai_settings', 'eveai_tenant_id');
|
||||||
|
register_setting('eveai_settings', 'eveai_api_key');
|
||||||
|
register_setting('eveai_settings', 'eveai_default_language');
|
||||||
|
register_setting('eveai_settings', 'eveai_excluded_categories');
|
||||||
|
register_setting('eveai_settings', 'eveai_excluded_categories');
|
||||||
|
register_setting('eveai_settings', 'eveai_access_token');
|
||||||
|
register_setting('eveai_settings', 'eveai_token_expiry');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_admin_menu() {
|
||||||
|
add_options_page(
|
||||||
|
'EveAI Sync Settings',
|
||||||
|
'EveAI Sync',
|
||||||
|
'manage_options',
|
||||||
|
'eveai-sync',
|
||||||
|
array($this, 'render_settings_page')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_settings_page() {
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||||
|
<form action="options.php" method="post">
|
||||||
|
<?php
|
||||||
|
settings_fields('eveai_settings');
|
||||||
|
do_settings_sections('eveai-sync');
|
||||||
|
?>
|
||||||
|
<table class="form-table">
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row">API URL</th>
|
||||||
|
<td><input type="text" name="eveai_api_url" value="<?php echo esc_attr(get_option('eveai_api_url')); ?>" style="width: 100%;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row">Tenant ID</th>
|
||||||
|
<td><input type="text" name="eveai_tenant_id" value="<?php echo esc_attr(get_option('eveai_tenant_id')); ?>" style="width: 100%;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row">API Key</th>
|
||||||
|
<td><input type="text" name="eveai_api_key" value="<?php echo esc_attr(get_option('eveai_api_key')); ?>" style="width: 100%;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row">Default Language</th>
|
||||||
|
<td><input type="text" name="eveai_default_language" value="<?php echo esc_attr(get_option('eveai_default_language', 'en')); ?>" style="width: 100%;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr valign="top">
|
||||||
|
<th scope="row">Excluded Categories</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="eveai_excluded_categories" value="<?php echo esc_attr(get_option('eveai_excluded_categories')); ?>" style="width: 100%;" />
|
||||||
|
<p class="description">Enter a comma-separated list of category names to exclude from syncing.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<?php submit_button('Save Settings'); ?>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Bulk Sync</h2>
|
||||||
|
<p>Click the button below to start a bulk sync of all posts and pages to EveAI.</p>
|
||||||
|
<form method="post" action="">
|
||||||
|
<?php wp_nonce_field('eveai_bulk_sync', 'eveai_bulk_sync_nonce'); ?>
|
||||||
|
<input type="submit" name="eveai_bulk_sync" class="button button-primary" value="Start Bulk Sync">
|
||||||
|
</form>
|
||||||
|
<div id="eveai-sync-results" style="margin-top: 20px;"></div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$('form').on('submit', function(e) {
|
||||||
|
if ($(this).find('input[name="eveai_bulk_sync"]').length) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $results = $('#eveai-sync-results');
|
||||||
|
$results.html('<p>Starting bulk sync...</p>');
|
||||||
|
$.ajax({
|
||||||
|
url: ajaxurl,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'eveai_bulk_sync',
|
||||||
|
nonce: '<?php echo wp_create_nonce('eveai_bulk_sync_ajax'); ?>'
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success) {
|
||||||
|
var resultsHtml = '<h3>Sync Results:</h3><ul>';
|
||||||
|
response.data.forEach(function(item) {
|
||||||
|
resultsHtml += '<li>' + item.title + ' (' + item.type + '): ' + item.status + '</li>';
|
||||||
|
});
|
||||||
|
resultsHtml += '</ul>';
|
||||||
|
$results.html(resultsHtml);
|
||||||
|
} else {
|
||||||
|
$results.html('<p>Error: ' + response.data + '</p>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
$results.html('<p>An error occurred. Please try again.</p>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_bulk_sync_ajax() {
|
||||||
|
check_ajax_referer('eveai_bulk_sync_ajax', 'nonce');
|
||||||
|
|
||||||
|
if (!current_user_can('manage_options')) {
|
||||||
|
wp_send_json_error('Insufficient permissions');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_handler = new EveAI_Post_Handler($this->api);
|
||||||
|
$bulk_sync = new EveAI_Bulk_Sync($this->api, $post_handler);
|
||||||
|
$results = $bulk_sync->init_bulk_sync();
|
||||||
|
|
||||||
|
wp_send_json_success($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_sync_meta_box() {
|
||||||
|
add_meta_box(
|
||||||
|
'eveai_sync_meta_box',
|
||||||
|
'EveAI Sync',
|
||||||
|
array($this, 'render_sync_meta_box'),
|
||||||
|
array('post', 'page'),
|
||||||
|
'side',
|
||||||
|
'default'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_sync_meta_box($post) {
|
||||||
|
$excluded = get_post_meta($post->ID, '_eveai_exclude_sync', true);
|
||||||
|
wp_nonce_field('eveai_sync_meta_box', 'eveai_sync_meta_box_nonce');
|
||||||
|
?>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="eveai_exclude_sync" value="1" <?php checked($excluded, '1'); ?>>
|
||||||
|
Exclude from EveAI sync
|
||||||
|
</label>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_bulk_sync() {
|
||||||
|
$post_handler = new EveAI_Post_Handler($this->api);
|
||||||
|
$bulk_sync = new EveAI_Bulk_Sync($this->api, $post_handler);
|
||||||
|
$bulk_sync->init_bulk_sync();
|
||||||
|
add_settings_error('eveai_messages', 'eveai_message', 'Bulk sync initiated successfully.', 'updated');
|
||||||
|
}
|
||||||
|
}
|
||||||
135
integrations/Wordpress/eveai_sync/includes/class-eveai-api.php
Normal file
135
integrations/Wordpress/eveai_sync/includes/class-eveai-api.php
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class EveAI_API {
|
||||||
|
private $api_url;
|
||||||
|
private $tenant_id;
|
||||||
|
private $api_key;
|
||||||
|
private $access_token;
|
||||||
|
private $token_expiry;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->api_url = get_option('eveai_api_url');
|
||||||
|
$this->tenant_id = get_option('eveai_tenant_id');
|
||||||
|
$this->api_key = get_option('eveai_api_key');
|
||||||
|
$this->access_token = get_option('eveai_access_token');
|
||||||
|
$this->token_expiry = get_option('eveai_token_expiry', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ensure_valid_token() {
|
||||||
|
if (empty($this->access_token) || time() > $this->token_expiry) {
|
||||||
|
$this->get_new_token();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_new_token() {
|
||||||
|
$response = wp_remote_post($this->api_url . '/api/v1/auth/token', [
|
||||||
|
'body' => json_encode([
|
||||||
|
'tenant_id' => $this->tenant_id,
|
||||||
|
'api_key' => $this->api_key,
|
||||||
|
]),
|
||||||
|
'headers' => [
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
throw new Exception('Failed to get token: ' . $response->get_error_message());
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
|
||||||
|
// Check if the body is already an array (decoded JSON)
|
||||||
|
if (!is_array($body)) {
|
||||||
|
$body = json_decode($body, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($body['access_token'])) {
|
||||||
|
throw new Exception('Invalid token response');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->access_token = $body['access_token'];
|
||||||
|
// Use the expiration time from the API response, or default to 1 hour if not provided
|
||||||
|
$expires_in = isset($body['expires_in']) ? $body['expires_in'] : 3600;
|
||||||
|
$this->token_expiry = time() + $expires_in - 10; // Subtract 10 seconds to be safe
|
||||||
|
|
||||||
|
update_option('eveai_access_token', $this->access_token);
|
||||||
|
update_option('eveai_token_expiry', $this->token_expiry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function make_request($method, $endpoint, $data = null) {
|
||||||
|
$this->ensure_valid_token();
|
||||||
|
|
||||||
|
error_log('EveAI API Request: ' . $method . ' ' . $this->api_url . $endpoint);
|
||||||
|
|
||||||
|
$url = $this->api_url . $endpoint;
|
||||||
|
|
||||||
|
$args = array(
|
||||||
|
'method' => $method,
|
||||||
|
'headers' => array(
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Authorization' => 'Bearer ' . $this->access_token,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($data !== null) {
|
||||||
|
$args['body'] = json_encode($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = wp_remote_request($url, $args);
|
||||||
|
|
||||||
|
if (is_wp_error($response)) {
|
||||||
|
$error_message = $response->get_error_message();
|
||||||
|
error_log('EveAI API Error: ' . $error_message);
|
||||||
|
throw new Exception('API request failed: ' . $error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = wp_remote_retrieve_body($response);
|
||||||
|
$status_code = wp_remote_retrieve_response_code($response);
|
||||||
|
|
||||||
|
error_log('EveAI API Response: ' . print_r($body, true));
|
||||||
|
error_log('EveAI API Status Code: ' . $status_code);
|
||||||
|
|
||||||
|
// Check if the body is already an array (decoded JSON)
|
||||||
|
if (!is_array($body)) {
|
||||||
|
$body = json_decode($body, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($status_code == 401) {
|
||||||
|
// Token might have expired, try to get a new one and retry the request
|
||||||
|
error_log('Token expired, trying to get a new one...');
|
||||||
|
$this->get_new_token();
|
||||||
|
return $this->make_request($method, $endpoint, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($status_code >= 400) {
|
||||||
|
$error_message = isset($body['message']) ? $body['message'] : 'Unknown error';
|
||||||
|
error_log('EveAI API Error: ' . $error_message);
|
||||||
|
throw new Exception('API error: ' . $error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_url($data) {
|
||||||
|
return $this->make_request('POST', '/api/v1/documents/add_url', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update_document($document_id, $data) {
|
||||||
|
return $this->make_request('PUT', "/api/v1/documents/{$document_id}", $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function invalidate_document($document_id) {
|
||||||
|
$data = array(
|
||||||
|
'valid_to' => gmdate('Y-m-d\TH:i:s\Z') // Current UTC time in ISO 8601 format
|
||||||
|
);
|
||||||
|
return $this->make_request('PUT', "/api/v1/documents/{$document_id}", $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refresh_document($document_id) {
|
||||||
|
return $this->make_request('POST', "/api/v1/documents/{$document_id}/refresh");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refresh_document_with_info($document_id, $data) {
|
||||||
|
return $this->make_request('POST', "/api/v1/documents/{$document_id}/refresh_with_info", $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class EveAI_Bulk_Sync {
|
||||||
|
private $api;
|
||||||
|
private $post_handler;
|
||||||
|
|
||||||
|
public function __construct($api, $post_handler) {
|
||||||
|
$this->api = $api;
|
||||||
|
$this->post_handler = $post_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function init_bulk_sync() {
|
||||||
|
$posts = get_posts(array(
|
||||||
|
'post_type' => array('post', 'page'),
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'posts_per_page' => -1,
|
||||||
|
));
|
||||||
|
|
||||||
|
$sync_results = array();
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$evie_id = get_post_meta($post->ID, '_eveai_document_id', true);
|
||||||
|
$evie_version_id = get_post_meta($post->ID, '_eveai_document_version_id', true);
|
||||||
|
$is_update = ($evie_id && $evie_version_id);
|
||||||
|
|
||||||
|
$result = $this->post_handler->sync_post($post->ID, $is_update);
|
||||||
|
$sync_results[] = array(
|
||||||
|
'id' => $post->ID,
|
||||||
|
'title' => $post->post_title,
|
||||||
|
'type' => $post->post_type,
|
||||||
|
'status' => $result ? 'success' : 'failed'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sync_results;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class EveAI_Post_Handler {
|
||||||
|
private $api;
|
||||||
|
|
||||||
|
public function __construct($api) {
|
||||||
|
$this->api = $api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_post_save($post_id, $post, $update) {
|
||||||
|
// Verify if this is not an auto save routine.
|
||||||
|
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
||||||
|
|
||||||
|
// Check if this is a revision
|
||||||
|
if (wp_is_post_revision($post_id)) return;
|
||||||
|
|
||||||
|
// Check if post type is one we want to sync
|
||||||
|
if (!in_array($post->post_type, ['post', 'page'])) return;
|
||||||
|
|
||||||
|
// Check if post status is published
|
||||||
|
if ($post->post_status != 'publish') return;
|
||||||
|
|
||||||
|
// Verify nonce
|
||||||
|
if (!isset($_POST['eveai_sync_meta_box_nonce']) || !wp_verify_nonce($_POST['eveai_sync_meta_box_nonce'], 'eveai_sync_meta_box')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check permissions
|
||||||
|
if ('page' == $_POST['post_type']) {
|
||||||
|
if (!current_user_can('edit_page', $post_id)) return;
|
||||||
|
} else {
|
||||||
|
if (!current_user_can('edit_post', $post_id)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should sync this post
|
||||||
|
if (!$this->should_sync_post($post_id)) return;
|
||||||
|
|
||||||
|
// Check if this is a REST API request
|
||||||
|
if (defined('REST_REQUEST') && REST_REQUEST) {
|
||||||
|
error_log("EveAI: REST API request detected for post $post_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log('Handling post' . $post_id . 'save event with update: ' . $update);
|
||||||
|
|
||||||
|
// Check if we've already synced this post in this request
|
||||||
|
if (get_post_meta($post_id, '_eveai_syncing', true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a flag to indicate we're syncing
|
||||||
|
update_post_meta($post_id, '_eveai_syncing', true);
|
||||||
|
|
||||||
|
$this->sync_post($post_id, $update);
|
||||||
|
|
||||||
|
// Remove the flag after syncing
|
||||||
|
delete_post_meta($post_id, '_eveai_syncing');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sync_post($post_id, $is_update) {
|
||||||
|
$evie_id = get_post_meta($post_id, '_eveai_document_id', true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($evie_id && $is_update) {
|
||||||
|
$old_data = $this->get_old_post_data($post_id);
|
||||||
|
$new_data = $this->prepare_post_data($post_id);
|
||||||
|
|
||||||
|
if ($this->has_metadata_changed($old_data, $new_data)) {
|
||||||
|
$result = $this->refresh_document_with_info($evie_id, $new_data);
|
||||||
|
} else {
|
||||||
|
$result = $this->refresh_document($evie_id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$data = $this->prepare_post_data($post_id);
|
||||||
|
$result = $this->api->add_url($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($result['document_id']) && isset($result['document_version_id'])) {
|
||||||
|
update_post_meta($post_id, '_eveai_document_id', $result['document_id']);
|
||||||
|
update_post_meta($post_id, '_eveai_document_version_id', $result['document_version_id']);
|
||||||
|
|
||||||
|
// Add debugging
|
||||||
|
error_log("EveAI: Set document_id {$result['document_id']} and document_version_id {$result['document_version_id']} for post {$post_id}");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('EveAI Sync Error: ' . $e->getMessage());
|
||||||
|
// Optionally, you can add an admin notice here
|
||||||
|
add_action('admin_notices', function() use ($e) {
|
||||||
|
echo '<div class="notice notice-error is-dismissible">';
|
||||||
|
echo '<p>EveAI Sync Error: ' . esc_html($e->getMessage()) . '</p>';
|
||||||
|
echo '</div>';
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_old_post_data($post_id) {
|
||||||
|
$post = get_post($post_id);
|
||||||
|
return array(
|
||||||
|
'name' => $post->post_title,
|
||||||
|
'system_metadata' => json_encode([
|
||||||
|
'post_id' => $post_id,
|
||||||
|
'type' => $post->post_type,
|
||||||
|
'author' => get_the_author_meta('display_name', $post->post_author),
|
||||||
|
'categories' => $post->post_type === 'post' ? wp_get_post_categories($post_id, array('fields' => 'names')) : [],
|
||||||
|
'tags' => $post->post_type === 'post' ? wp_get_post_tags($post_id, array('fields' => 'names')) : [],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function has_metadata_changed($old_data, $new_data) {
|
||||||
|
return $old_data['name'] !== $new_data['name'] ||
|
||||||
|
$old_data['user_metadata'] !== $new_data['user_metadata'];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function refresh_document_with_info($evie_id, $data) {
|
||||||
|
try {
|
||||||
|
return $this->api->refresh_document_with_info($evie_id, $data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('EveAI refresh with info error: ' . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function refresh_document($evie_id) {
|
||||||
|
try {
|
||||||
|
return $this->api->refresh_document($evie_id);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('EveAI refresh error: ' . $e->getMessage());
|
||||||
|
add_action('admin_notices', function() use ($e) {
|
||||||
|
echo '<div class="notice notice-error is-dismissible">';
|
||||||
|
echo '<p>EveAI Sync Error: ' . esc_html($e->getMessage()) . '</p>';
|
||||||
|
echo '</div>';
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle_post_delete($post_id) {
|
||||||
|
if ($evie_id) {
|
||||||
|
try {
|
||||||
|
$this->api->invalidate_document($evie_id);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('EveAI invalidate error: ' . $e->getMessage());
|
||||||
|
add_action('admin_notices', function() use ($e) {
|
||||||
|
echo '<div class="notice notice-error is-dismissible">';
|
||||||
|
echo '<p>EveAI Sync Error: ' . esc_html($e->getMessage()) . '</p>';
|
||||||
|
echo '</div>';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process_sync_queue() {
|
||||||
|
$queue = get_option('eveai_sync_queue', array());
|
||||||
|
foreach ($queue as $key => $item) {
|
||||||
|
$this->sync_post($item['post_id'], $item['is_update']);
|
||||||
|
unset($queue[$key]);
|
||||||
|
}
|
||||||
|
update_option('eveai_sync_queue', $queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function should_sync_post($post_id) {
|
||||||
|
if (get_post_meta($post_id, '_eveai_exclude_sync', true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post_type = get_post_type($post_id);
|
||||||
|
|
||||||
|
if ($post_type === 'page') {
|
||||||
|
// Pages are always synced unless individually excluded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($post_type === 'post') {
|
||||||
|
$excluded_categories_string = get_option('eveai_excluded_categories', '');
|
||||||
|
$excluded_categories = array_map('trim', explode(',', $excluded_categories_string));
|
||||||
|
|
||||||
|
$post_categories = wp_get_post_categories($post_id, array('fields' => 'names'));
|
||||||
|
$post_tags = wp_get_post_tags($post_id, array('fields' => 'names'));
|
||||||
|
|
||||||
|
// Check if any of the post's categories or tags are not in the excluded list
|
||||||
|
$all_terms = array_merge($post_categories, $post_tags);
|
||||||
|
foreach ($all_terms as $term) {
|
||||||
|
if (!in_array($term, $excluded_categories)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepare_post_data($post_id) {
|
||||||
|
$post = get_post($post_id);
|
||||||
|
$data = array(
|
||||||
|
'url' => get_permalink($post_id),
|
||||||
|
'name' => $post->post_title,
|
||||||
|
'language' => get_option('eveai_default_language', 'en'),
|
||||||
|
'valid_from' => get_gmt_from_date($post->post_date, 'Y-m-d\TH:i:s\Z'),
|
||||||
|
'user_metadata' => json_encode([
|
||||||
|
'post_id' => $post_id,
|
||||||
|
'type' => $post->post_type,
|
||||||
|
'author' => get_the_author_meta('display_name', $post->post_author),
|
||||||
|
'categories' => $post->post_type === 'post' ? wp_get_post_categories($post_id, array('fields' => 'names')) : [],
|
||||||
|
'tags' => $post->post_type === 'post' ? wp_get_post_tags($post_id, array('fields' => 'names')) : [],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class EveAI_Sync {
|
||||||
|
private $api;
|
||||||
|
private $post_handler;
|
||||||
|
private $admin;
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
$this->load_dependencies();
|
||||||
|
$this->setup_actions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function load_dependencies() {
|
||||||
|
require_once EVEAI_SYNC_PLUGIN_DIR . 'includes/class-eveai-api.php';
|
||||||
|
require_once EVEAI_SYNC_PLUGIN_DIR . 'includes/class-eveai-post-handler.php';
|
||||||
|
require_once EVEAI_SYNC_PLUGIN_DIR . 'includes/class-eveai-admin.php';
|
||||||
|
require_once EVEAI_SYNC_PLUGIN_DIR . 'includes/class-eveai-bulk-sync.php';
|
||||||
|
|
||||||
|
$this->api = new EveAI_API();
|
||||||
|
$this->post_handler = new EveAI_Post_Handler($this->api);
|
||||||
|
$this->admin = new EveAI_Admin($this->api);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setup_actions() {
|
||||||
|
add_action('save_post', array($this->post_handler, 'handle_post_save'), 10, 3);
|
||||||
|
add_action('before_delete_post', array($this->post_handler, 'handle_post_delete'));
|
||||||
|
add_action('admin_init', array($this->admin, 'register_settings'));
|
||||||
|
add_action('admin_menu', array($this->admin, 'add_admin_menu'));
|
||||||
|
add_action('add_meta_boxes', array($this->admin, 'add_sync_meta_box'));
|
||||||
|
add_action('eveai_sync_post', array($this->post_handler, 'sync_post'), 10, 2);
|
||||||
|
add_action('wp_ajax_eveai_bulk_sync', array($this->admin, 'handle_bulk_sync_ajax'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -137,6 +137,27 @@ http {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://eveai_api:5003;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Prefix /api;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_buffer_size 16k;
|
||||||
|
proxy_buffers 4 32k;
|
||||||
|
proxy_busy_buffers_size 64k;
|
||||||
|
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
send_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
location /flower/ {
|
location /flower/ {
|
||||||
proxy_pass http://127.0.0.1:5555/;
|
proxy_pass http://127.0.0.1:5555/;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|||||||
Reference in New Issue
Block a user