Files
2026-04-23 04:33:43 +03:30

181 lines
13 KiB
PHP

<?php
namespace CropLogic;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Settings {
const OPTION_KEY = 'crop_logic_options';
/**
* @var Settings|null
*/
private static $instance = null;
public static function instance(): Settings {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action( 'admin_menu', [ $this, 'register_menu' ] );
add_action( 'admin_init', [ $this, 'register_settings' ] );
}
public static function defaults(): array {
return [
'provider' => 'custom_json',
'endpoint_url' => 'http://backend-web:8000',
'api_key' => '',
'model' => '',
'system_prompt' => '',
'temperature' => '0.7',
'timeout' => '25',
'custom_headers' => '',
'create_thread_path' => '/api/farm-ai-assistant/chats/',
'list_threads_path' => '/api/farm-ai-assistant/chats/',
'delete_thread_path' => '/api/farm-ai-assistant/chats/{conversation_id}/',
'get_messages_path' => '/api/farm-ai-assistant/chats/{conversation_id}/messages/',
'create_message_path' => '/api/farm-ai-assistant/chat/task/',
'task_status_path' => '/api/farm-ai-assistant/chat/task/{task_id}/status/',
'verify_path' => '/api/auth/verify/',
'login_path' => '/api/auth/login/',
'register_path' => '/api/auth/register/',
'forgot_path' => '/api/auth/forgot-password/',
'reset_path' => '/api/auth/reset-password/',
'token_header' => 'Authorization',
'token_prefix' => 'Bearer ',
];
}
public static function get_options(): array {
$options = get_option( self::OPTION_KEY, [] );
if ( ! is_array( $options ) ) {
$options = [];
}
return wp_parse_args( $options, self::defaults() );
}
public function register_menu(): void {
add_menu_page(
__( 'تنظیمات Crop Logic', 'crop-logic' ),
__( 'Crop Logic', 'crop-logic' ),
'manage_options',
'crop-logic',
[ $this, 'render_page' ],
'dashicons-format-chat',
56
);
}
public function register_settings(): void {
register_setting(
'crop_logic',
self::OPTION_KEY,
[
'type' => 'array',
'sanitize_callback' => [ $this, 'sanitize_options' ],
'default' => self::defaults(),
]
);
}
public function sanitize_options( $input ): array {
$defaults = self::defaults();
$input = is_array( $input ) ? $input : [];
$options = [];
$options['provider'] = in_array( $input['provider'] ?? '', [ 'custom_json', 'openai_compatible' ], true ) ? $input['provider'] : $defaults['provider'];
$options['endpoint_url'] = esc_url_raw( trim( (string) ( $input['endpoint_url'] ?? '' ) ) );
$options['api_key'] = sanitize_text_field( (string) ( $input['api_key'] ?? '' ) );
$options['model'] = sanitize_text_field( (string) ( $input['model'] ?? '' ) );
$options['system_prompt'] = sanitize_textarea_field( (string) ( $input['system_prompt'] ?? '' ) );
$options['custom_headers'] = trim( (string) ( $input['custom_headers'] ?? '' ) );
$options['create_thread_path'] = $this->sanitize_path_template( (string) ( $input['create_thread_path'] ?? $defaults['create_thread_path'] ) );
$options['list_threads_path'] = $this->sanitize_path_template( (string) ( $input['list_threads_path'] ?? $defaults['list_threads_path'] ) );
$options['delete_thread_path'] = $this->sanitize_path_template( (string) ( $input['delete_thread_path'] ?? $defaults['delete_thread_path'] ) );
$options['get_messages_path'] = $this->sanitize_path_template( (string) ( $input['get_messages_path'] ?? $defaults['get_messages_path'] ) );
$options['create_message_path'] = $this->sanitize_path_template( (string) ( $input['create_message_path'] ?? $defaults['create_message_path'] ) );
$options['task_status_path'] = $this->sanitize_path_template( (string) ( $input['task_status_path'] ?? $defaults['task_status_path'] ) );
$options['verify_path'] = $this->sanitize_path_template( (string) ( $input['verify_path'] ?? $defaults['verify_path'] ) );
$options['login_path'] = $this->sanitize_path_template( (string) ( $input['login_path'] ?? $defaults['login_path'] ) );
$options['register_path'] = $this->sanitize_path_template( (string) ( $input['register_path'] ?? $defaults['register_path'] ) );
$options['forgot_path'] = $this->sanitize_path_template( (string) ( $input['forgot_path'] ?? $defaults['forgot_path'] ) );
$options['reset_path'] = $this->sanitize_path_template( (string) ( $input['reset_path'] ?? $defaults['reset_path'] ) );
$options['token_header'] = sanitize_text_field( (string) ( $input['token_header'] ?? $defaults['token_header'] ) );
$options['token_prefix'] = (string) ( $input['token_prefix'] ?? $defaults['token_prefix'] );
$temperature = is_numeric( $input['temperature'] ?? null ) ? (float) $input['temperature'] : (float) $defaults['temperature'];
$timeout = is_numeric( $input['timeout'] ?? null ) ? (int) $input['timeout'] : (int) $defaults['timeout'];
$options['temperature'] = (string) max( 0, min( 2, $temperature ) );
$options['timeout'] = (string) max( 5, min( 120, $timeout ) );
if ( '' !== $options['custom_headers'] ) {
$json = json_decode( $options['custom_headers'], true );
if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $json ) ) {
$options['custom_headers'] = '';
add_settings_error( self::OPTION_KEY, 'crop_logic_headers', __( 'هدرهای سفارشی باید JSON معتبر باشند.', 'crop-logic' ) );
}
}
return wp_parse_args( $options, $defaults );
}
private function sanitize_path_template( string $path ): string {
$path = trim( $path );
if ( '' === $path ) {
return '/';
}
if ( '/' !== substr( $path, 0, 1 ) ) {
$path = '/' . $path;
}
return preg_replace( '#/+#', '/', $path ) ?: '/';
}
public function render_page(): void {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$options = self::get_options();
?>
<div class="wrap">
<h1><?php echo esc_html__( 'تنظیمات API پلاگین Crop Logic', 'crop-logic' ); ?></h1>
<p><?php echo esc_html__( 'APIها بر اساس راهنمای Farm AI Assistant و Auth تنظیم شده‌اند و وردپرس فقط نقش واسط را دارد.', 'crop-logic' ); ?></p>
<?php settings_errors( self::OPTION_KEY ); ?>
<form action="options.php" method="post">
<?php settings_fields( 'crop_logic' ); ?>
<table class="form-table" role="presentation">
<tr><th scope="row"><label for="crop_logic_endpoint_url"><?php echo esc_html__( 'آدرس پایه API', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_endpoint_url" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[endpoint_url]" type="url" value="<?php echo esc_attr( $options['endpoint_url'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_api_key"><?php echo esc_html__( 'کلید API', 'crop-logic' ); ?></label></th><td><input class="regular-text" id="crop_logic_api_key" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[api_key]" type="password" value="<?php echo esc_attr( $options['api_key'] ); ?>" autocomplete="off" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_timeout"><?php echo esc_html__( 'مهلت پاسخ', 'crop-logic' ); ?></label></th><td><input class="small-text" id="crop_logic_timeout" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[timeout]" type="number" min="5" max="120" step="1" value="<?php echo esc_attr( $options['timeout'] ); ?>" dir="ltr" /> <span><?php echo esc_html__( 'ثانیه', 'crop-logic' ); ?></span></td></tr>
<tr><th scope="row"><label for="crop_logic_custom_headers"><?php echo esc_html__( 'هدرهای سفارشی', 'crop-logic' ); ?></label></th><td><textarea class="large-text code" id="crop_logic_custom_headers" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[custom_headers]" rows="4" dir="ltr"><?php echo esc_textarea( $options['custom_headers'] ); ?></textarea></td></tr>
<tr><th colspan="2"><h2><?php echo esc_html__( 'مسیرهای احراز هویت', 'crop-logic' ); ?></h2></th></tr>
<tr><th scope="row"><label for="crop_logic_verify_path"><?php echo esc_html__( 'verify', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_verify_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[verify_path]" type="text" value="<?php echo esc_attr( $options['verify_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_login_path"><?php echo esc_html__( 'login', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_login_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[login_path]" type="text" value="<?php echo esc_attr( $options['login_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_register_path"><?php echo esc_html__( 'register', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_register_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[register_path]" type="text" value="<?php echo esc_attr( $options['register_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_forgot_path"><?php echo esc_html__( 'forgot password', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_forgot_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[forgot_path]" type="text" value="<?php echo esc_attr( $options['forgot_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_reset_path"><?php echo esc_html__( 'reset password', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_reset_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[reset_path]" type="text" value="<?php echo esc_attr( $options['reset_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th colspan="2"><h2><?php echo esc_html__( 'مسیرهای چت', 'crop-logic' ); ?></h2></th></tr>
<tr><th scope="row"><label for="crop_logic_list_threads_path"><?php echo esc_html__( 'لیست گفتگوها', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_list_threads_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[list_threads_path]" type="text" value="<?php echo esc_attr( $options['list_threads_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_create_thread_path"><?php echo esc_html__( 'ساخت گفتگوی خالی', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_create_thread_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[create_thread_path]" type="text" value="<?php echo esc_attr( $options['create_thread_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_get_messages_path"><?php echo esc_html__( 'دریافت پیام‌ها', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_get_messages_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[get_messages_path]" type="text" value="<?php echo esc_attr( $options['get_messages_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_create_message_path"><?php echo esc_html__( 'ساخت task پیام', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_create_message_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[create_message_path]" type="text" value="<?php echo esc_attr( $options['create_message_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_task_status_path"><?php echo esc_html__( 'وضعیت task', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_task_status_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[task_status_path]" type="text" value="<?php echo esc_attr( $options['task_status_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_delete_thread_path"><?php echo esc_html__( 'حذف گفتگو', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_delete_thread_path" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[delete_thread_path]" type="text" value="<?php echo esc_attr( $options['delete_thread_path'] ); ?>" dir="ltr" /></td></tr>
<tr><th colspan="2"><h2><?php echo esc_html__( 'توکن کاربر', 'crop-logic' ); ?></h2></th></tr>
<tr><th scope="row"><label for="crop_logic_token_header"><?php echo esc_html__( 'نام هدر توکن', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_token_header" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[token_header]" type="text" value="<?php echo esc_attr( $options['token_header'] ); ?>" dir="ltr" /></td></tr>
<tr><th scope="row"><label for="crop_logic_token_prefix"><?php echo esc_html__( 'پیشوند توکن', 'crop-logic' ); ?></label></th><td><input class="regular-text code" id="crop_logic_token_prefix" name="<?php echo esc_attr( self::OPTION_KEY ); ?>[token_prefix]" type="text" value="<?php echo esc_attr( $options['token_prefix'] ); ?>" dir="ltr" /></td></tr>
</table>
<?php submit_button( __( 'ذخیره تنظیمات', 'crop-logic' ) ); ?>
</form>
</div>
<?php
}
}