{% extends "admin/change_form.html" %}
{% load static %}
{% block content_title %}{% endblock %} 
{% block object-tools %}
{% endblock %}
{% block body_main %}
<div class="card  mb-3">
    <div class="card-header">API操作</div>  
    <div class="card-body">
        <div class="row g-3">
            <div class="alert alert-info">
                <p>API操作可用于get触发操作，须进行安全码校验</p>
                <p>API中如果需要host，网站基础设置的网址优先，其次才是自动获取。</p>
            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'remove_static_file' %}?code={{code}}' data-method="GET"> <span class="btn-text">移除整站静态</span></a></p>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'remove_static_file' %}?code={{code}}</code></pre>
                        <ul class="small text-secondary">
                            <li>在静态设置中设置使用静态后，网站会生成很多静态文件，但是这些文件在长时间后并不一定都会有复访，所以我们可以通过宝塔计划任务进行get触发来清除这些静态。这并不影响新的页面生成。</li>     
                            <li>注意的是，在使用静态后，如果在非调试情况下，你在有限时间内的页面须清除静态才可以看到新的页面</li>                
                        </ul>
                    </div>
                </div>
  
            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'article-update-status99' %}?code={{code}}' data-method="GET"> <span class="btn-text">发布文章</span></a></p>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'article-update-status99' %}?code={{code}}</code></pre>
                        <p class="text-secondary">如果你在URL推送设置当中设置了文章API发布时进行URL推送，请保持host与你在搜索引擎当中绑定的域名是同样的。</p>
                        <p class="text-center small"><button  class="btn btn-link btn-sm" onclick="showLogModal('article_update_status', {autoRefresh: false})">API发布文章日志</button></p>
                    </div>
                </div>

            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'create_ai_article' %}?code={{code}}' data-method="GET"> <span class="btn-text">AI生成文章</span></a></p>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'create_ai_article' %}?code={{code}}</code></pre>
                        <p class="text-secondary">通过宝塔的计划任务可以get触发AI生成。</p>
                        <p class="small text-center"><button  class="btn btn-link btn-sm" onclick="showLogModal('ai_create', {autoRefresh: false})">AI生成日志</button></p>

                    </div>
                </div>

            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'restart_sys' %}?code={{code}}' data-method="GET"> <span class="btn-text">重启系统</span></a></p>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'restart_sys' %}?code={{code}}</code></pre>
                        <ul class="text-secondary">
                            <li>linux可实现完美重启</li>
                            <li>window:我们多数做为开发环境，不推荐做为生产环境。</li>                            
                        </ul>
                    </div>
                </div>

            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'collect_static' %}?code={{code}}' data-method="GET"> <span class="btn-text">收集静态文件</span></a></p>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'collect_static' %}?code={{code}}</code></pre>
                        <ul class="small text-secondary">
                            <li>可将整站的静态文件收集于“<span class="fw-bold" >collected_static</span>”路径，用于统一管理。通常用于模板修改后的静态文件收集。如果遇到相对于静态路径设置处理后同路径同名的文件，只收集第一个遇到的文件。对于静态路径的解释可参详官方文档。</li>
                            <li>初始化时我们会自动进行一次静态收集，每次打开主题设置的时候也会进行静态收集。<strong>但此处的API操作会执行请除陈旧静态后，再收集静态，可以保证文件为最新的。</strong></li>
                            
                        </ul>
                    </div>
                </div>

            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'tag-import' %}?code={{code}}' data-method="GET"> <span class="btn-text">TAG导入</span></a></p>
                        
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'tag-import' %}?code={{code}}</code></pre>
                        <ul class="small text-secondary">                         
                            <li>文件路径：须将名为tag_words.txt放入到路径/static/tag_words/下，如果没有文件夹则新建。须每行一个词，编码为utf-8</li>
                            <li>测试效果：<strong>20万词导入时间27.1秒</strong>。须注意如果词太多可能会造成502超时，此时可以偶尔刷新一下tag列表或者数据库再或者看一下tag_words.txt中的词语变化，也可以在项目日志中查看有关导入进度的提示。</li>
                            <li>程序在执行导入时，会自动将数据库中tags导出到/static/tag_words/tag_word_out_<时间戳>.txt中，此文件程序不会自动清除，如有需要可手动删除。导入过程会与此文件进行对比去重，也会自身去重，以此保证加入不重复的tag.Title</li>
                            <li class="fw-bold">目前我们线上实际最多的tag为1800万，运行内存800M左右，前端秒开。</li>
                            <li class="fw-bold">tag导入是非常吃资源的操作，请在闲时执行。执行完后，最好重启项目</li>

                        </ul>
                        <p class="text-center small"><button class="btn btn-link btn-sm" onclick="showLogModal('tag_import', {showProgress: true, refreshInterval: 5000})">查看标签导入进度</button></p>   
                    </div>
                </div>

            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <h3 class="text-center">文章上传接口</h3>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'article-create' %}</code></pre>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>data：</strong>{'code':'{{code}}','title': 'title', 'content': 'content','status':0,'category_id':1,'meta_description':'des','meta_keywords':'keyword','editor_id':1}</code></pre>
                        <ul class="small text-secondary">                           
                            <li>左侧为我们的字段，右侧为获取的值。</li>  
                            <li>如果使用soogor Factory可以直接使用该接口，修改category_id就可以上传，editor_id为用户ID。</li>
                            <li>如果使用其他支持API发布的软件如火车头或者蓝天发布器之类的，需要将右侧换为软件采集时的字段。</li>
                            <li>status状态码说明：0：已入库； 1：草稿；2：已提交；3：审核中；4：已通过；5：已驳回；6：已归档；99：已发布。</li>
                        </ul>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>成功：</strong>
{
    'code': 201,
    'message': 'Article uploaded successfully',
    'data': ''
}</code></pre>
                        
                    </div>
                </div>

            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><a href="#"  class="btn btn-ajax-action btn-outline-secondary"  data-type='api' data-url='{% url 'reset_secret_key' %}?code={{code}}' data-method="GET"> <span class="btn-text">重置SECRET_KEY</span></a></p>
                        <pre class="bg-secondary-subtle border border-1 p-3 rounded"><code><strong>url：</strong>{{full_host}}{% url 'reset_secret_key' %}?code={{code}}</code></pre>
                        <ul class="text-secondary">
                            <li>SECRET_KEY是DjacoreCMS重要的安全密钥，使用默认或者长期使用一个SECRET_KEY是非常不利于网站健康的。为此系统有自己的密钥轮换机制，该API是用于用户手动更新SECRET_KEY的。</li>
                            <li><strong>所有用户会话将立即失效</strong></li>
                            <li>需要重新登录系统</li>
                            <li>系统将自动重启应用</li>
                        </ul>
                    </div>
                </div>

            </div>
        </div>
        
    </div>
</div>
<!--
<div class="card  mb-3">
    <div class="card-header">日志操作</div>
    <div class="card-body">
        <div class="alert alert-info">日志操作仅用于后台查看。</div>
        <div class="row">
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><button  class="btn btn-outline-secondary" onclick="showLogModal('ai_create', {autoRefresh: false})">AI生成日志</button></p>
                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <p class="text-center"><button  class="btn btn-outline-secondary" onclick="showLogModal('article_update_status', {autoRefresh: false})">API发布文章日志</button></p>
                    </div>
                </div>

            </div>
        </div>        
    </div>
</div>-->

<!-- 增强版全局模态框 -->
<div class="modal fade" id="global_commonModal" data-bs-backdrop="static">
    <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg">
        <div class="modal-content border-3 shadow-lg">
            <div class="modal-header bg-primary-subtle">
                <h4 class="modal-title"><i class="fas fa-file-code me-2"></i><span id="modalHeader"></span></h4>
                <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body" >
                <div class="container-fluid">
                    <div id="modalLoader" class="text-center py-4">
                        <div class="spinner-grow text-primary" role="status">
                            <span class="visually-hidden">Loading...</span>
                        </div>
                        <p class="text-muted mt-2">文件加载中...</p>
                    </div>
                    <div id="modalMain" style="display: none;">
                        <!-- 动态内容容器 -->
                    </div>
                </div>
            </div>
            <div class="modal-footer bg-light">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
                    <i class="fas fa-times me-1"></i>关闭
                </button>
            </div>
        </div>
    </div>
  </div>
  <script>
    (function($) {
        const myModal = new bootstrap.Modal('#global_commonModal', {
            keyboard: false
        });
    
        $('.btn-ajax-action').click(function() {
            const $btn = $(this);
            const btnText = $btn.find('.btn-text').text();
            
            // 初始化模态框状态
            $('#modalHeader').html(btnText);
            $('#modalMain').empty().hide();
            $('#modalLoader').show();
            myModal.show();
    
            $.ajax({
                url: $btn.data('url'),
                method: $btn.data('method') || 'GET',
                headers: { 
                    'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val() 
                },
                dataType: 'json'
            }).done(function(response, textStatus, xhr) {
                // 统一使用HTTP状态码判断（替代response.status）
                if (xhr.status === 200) {   
                    // 根据按钮类型处理不同内容
                    if ($btn.data('type') === 'logs') {
                        handleLogsResponse(response);
                    } else {
                        handleDefaultResponse(response);
                    }
                }
            }).fail(function(xhr) {
                // 1. 尝试解析响应体，获取更详细的业务错误信息
                let errorDetail = ''; // 用于存储从响应体中提取的错误信息
                let responseObj = null;

                // 尝试将响应文本解析为JSON对象
                try {
                    // 检查xhr.responseText是否存在且不为空
                    if (xhr.responseText) {
                        responseObj = JSON.parse(xhr.responseText);
                    }
                } catch (e) {
                    // 如果解析失败，说明响应体可能不是JSON或格式不正确，忽略即可，我们将使用HTTP状态码信息
                    console.warn('响应体不是有效的JSON格式，将回退到HTTP状态码错误信息。');
                }

                // 2. 优先从响应体对象中查找业务错误信息
                if (responseObj) {
                    // 检查是否存在 'error' 或 'message' 字段
                    if (responseObj.error) {
                        errorDetail = responseObj.error;
                    } else if (responseObj.message) {
                        errorDetail = responseObj.message;
                    }
                    // 如果以后需要其他字段，可以在此扩展，例如：
                    // else if (responseObj.detail) { errorDetail = responseObj.detail; }
                }

                // 3. 根据不同的错误情况组合最终显示给用户的错误信息
                let finalErrorMessage = '';
                
                // 处理特定的HTTP状态码错误
                if (xhr.status === 502) {
                    finalErrorMessage = `请求超时或服务器暂时不可用 (502 Bad Gateway)<br>该操作可能用时较长，请通过终端或系统日志查看具体情况`;
                } 
                // 处理业务逻辑错误（HTTP状态码可能是200，但响应体包含error）
                // 注意：如果你的业务错误通常也伴随着非2xx的状态码（如400, 500等），这个else if分支可能更合适
                else if (errorDetail) {
                    // 如果从响应体中提取到了业务错误信息，优先显示它
                    finalErrorMessage = `请求失败: ${errorDetail}`;
                    // 可以附加上状态码信息，提供更多上下文
                    if (xhr.status !== 200) { // 如果业务错误时状态码通常还是200，则可以省略状态码显示
                        finalErrorMessage += ` (状态码: ${xhr.status})`;
                    }
                } 
                // 处理其他未明确分类的错误（既不是502，响应体也没有业务错误信息）
                else {
                    finalErrorMessage = `请求失败: ${xhr.statusText} (${xhr.status})`;
                }

                // 4. 显示最终的错误信息
                showError(finalErrorMessage);
            }).always(() => {
                $('#modalLoader').hide();
                $('#modalMain').show();
            });
        });
    
        /** 处理日志类型响应 */
        function handleLogsResponse(response) {
            try {
                if (!response?.logs || !Array.isArray(response.logs)) {
                    throw new Error('日志数据格式异常');
                }
                $('#modalMain').addClass('bg-dark text-white p-2')
                const $list = $('<ul class="log-list"></ul>');
                response.logs.slice(0, 100).forEach(log => {
                    $list.append(
                        `<li >${log}</li>`
                    );
                });
                $('#modalMain').empty().append($list);
                
            } catch (e) {
                console.error('日志渲染失败:', e);
                showError('日志解析失败，请联系管理员');
            }
        }
    
        /** 处理默认类型响应 */
        function handleDefaultResponse(response) {
            if (typeof response === 'object') {
                $('#modalMain').html(JSON.stringify(response, null, 2));
            } else {
                $('#modalMain').html(response);
            }
        }
    
        /** 统一错误展示 */
        function showError(msg) {
            $('#modalMain').html(
                `<div class="alert alert-danger">${msg}</div>`
            );
        }
    
    })(django.jQuery);
    </script>
    
{% include "admin/log_service/log_modals.html" %}
{% endblock %}
