<!DOCTYPE html>
<html lang="en">

<head>
    <base href="{{it.basePath}}">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <meta name="description" content="txAdmin - remotely Manage & Monitor your GTA5 FiveM Server">
    <meta name="author" content="André Tabarra">
    <title>Server Deployer</title>
    <link href="css/simple-line-icons.css" rel="stylesheet">
    <link href="css/coreui.min.css" rel="stylesheet">
    <link href="css/dark.css" rel="stylesheet">
    <link rel="shortcut icon" type="image/png" href="img/favicon_default.png" id="favicon" />
    <link rel="stylesheet" href="css/txAdmin.css">

    <!-- Page CSS -->
    <!-- <link rel="stylesheet" href="css/codemirror.css"> -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.61.1/codemirror.min.css" 
        integrity="sha512-xIf9AdJauwKIVtrVRZ0i4nHP61Ogx9fSRAkCLecmE2dL/U8ioWpDvFCAy4dcfecN72HHB9+7FfQj3aiO68aaaw==" 
        crossorigin="anonymous" referrerpolicy="no-referrer" />
    <link rel="stylesheet" href="css/codemirror_lucario.css">
    <style>
        .inline-code{
            color: #321fdb!important;
        }
        body.theme--dark .inline-code {
            color: #00bf8f !important;
        }
        .cm-s-lucario{
            font-size: 1.05rem !important;
        }
        .CodeMirror{
            height: auto;
        }
        .deployer-stepper > .card-header,
        .deployer-stepper > .card-footer{
            font-size: large;
        }
    </style>
    
    <!-- injected consts -->
    <script>{{it.jsInjection|safe}}</script>
</head>

<body class="c-app flex-row align-items-center {{it.uiTheme}}">

    <div class="container">

        <!-- Logo Row -->
        <div class="row justify-content-center pt-4 mb-4">
            <img src="img/txadmin.png" alt="txAdmin Logo">
        </div>

        <!-- Stepper Row -->
        <div class="row justify-content-center">
            <div class="col-md-9">
                <div class="card fade-in deployer-stepper">
                    {{@if(it.step == 'review')}}
                        <div class="card-header font-weight-bold">Step 1: Review Recipe</div>
                        <div class="card-body">
                            <div class="row mb-1 mt-0 text-center">
                                <div class="col-8 mx-auto card d-block border-primary p-2 mb-1">
                                    Please review the Recipe below and apply any changes you want, <br>
                                    then press the <strong>Run Recipe</strong> button below.
        
                                    {{@if(!it.recipe.isTrustedSource)}}
                                        <br><span class="text-danger font-weight-bold">Warning: Only run Recipes from trusted sources!</span>
                                    {{/if}}
                                </div>
                            </div>
                            <p class="mb-1">
                                <strong style="word-wrap: break-word;">{{it.recipe.name}}</strong>
                                {{@if(it.recipe.version != '')}}
                                    <em>{{it.recipe.version}}</em>
                                {{/if}}
                                {{@if(it.recipe.author != '')}}
                                    by {{it.recipe.author}}
                                {{/if}}
                                {{@if(it.recipe.description != '')}}
                                    <br>
                                    {{it.recipe.description}}
                                {{/if}}
                            </p>
        
                            <textarea id="codeMirrorTarget" style="width: 100%;" class="cms-s-lucario" name="code">{{it.recipe.raw}}</textarea>
                            <div class="row justify-content-center m-2">
                                <button class="btn btn-outline-danger" type="button" 
                                    onclick="cancelAction()">Cancel and Return to Setup</button>
                                &nbsp; &nbsp;
                                <button class="btn btn-info" type="button" 
                                    onclick="step1Action();">Next</button>
                            </div>
                        </div>
                        <div class="card-footer text-muted">Step 2: Input Parameters</div>
                        <div class="card-footer text-muted">Step 3: Run Recipe</div>
                        <div class="card-footer text-muted">Step 4: Configure server.cfg</div>
        
                    {{#elif(it.step == 'input')}}
                        <div class="card-header">Step 1: Review Recipe ✔️</div>
                        <div class="card-header font-weight-bold ">Step 2: Input Parameters</div>
                        <div class="card-body">
                            {{@if(it.defaults.autofilled)}}
                                <div class="row mb-3 mt-0 text-center">
                                    <div class="col-8 mx-auto card d-block border-warning p-2 mb-1">
                                        <strong>Note:</strong> The following config was autofilled by <strong>Zap-Hosting</strong>. <br>
                                        You <i>may</i> edit those, but it's strongly disencouraged.
                                    </div>
                                </div>
                            {{/if}}
                            <form id="step2-form">
                                <div class="form-group row">
                                    <label for="step2-svLicense" class="col-sm-3 col-form-label">
                                        License Key
                                        <span class="text-danger">*</span>
                                    </label>
                                    <div class="col-sm-9">
                                        <input type="text" class="form-control blur-input" id="step2-svLicense" maxlength="32" 
                                            autocomplete="off" placeholder="cfxk_xxxxxxxxxxxxxxxxxxxx_xxxxx" 
                                            value="{{it.defaults.license}}" required autofocus>
                                        <span class="form-text text-muted">
                                            The license key for your server obtained in <a href="https://keymaster.fivem.net/" target="_blank" rel="noopener noreferrer">Keymaster</a>.
                                        </span>
                                    </div>
                                </div>
                                {{@if(it.requireDBConfig)}}
                                    <div class="form-group row">
                                        <label for="step2-dbHost" class="col-sm-3 col-form-label">
                                            Database Host
                                            <span class="text-danger">*</span>
                                        </label>
                                        <div class="col-sm-9">
                                            <input type="text" class="form-control" id="step2-dbHost"
                                                placeholder="{{it.defaults.mysqlHost}}" value="{{it.defaults.mysqlHost}}" required>
                                            <span class="form-text text-muted">
                                                The IP/Hostname of the database server (usually <code>localhost</code>).
                                            </span>
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="step2-dbUsername" class="col-sm-3 col-form-label">
                                            Database Username
                                            <span class="text-danger">*</span>
                                        </label>
                                        <div class="col-sm-9">
                                            <input type="text" class="form-control" id="step2-dbUsername"
                                                placeholder="{{it.defaults.mysqlUser}}" value="{{it.defaults.mysqlUser}}" required>
                                            <span class="form-text text-muted">
                                                The database username (usually <code>root</code>).
                                            </span>
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="step2-dbPassword" class="col-sm-3 col-form-label">
                                            Database Password
                                        </label>
                                        <div class="col-sm-9">
                                            <input type="text" class="form-control blur-input" id="step2-dbPassword"
                                                autocomplete="off" placeholder="leave it blank" value="{{it.defaults.mysqlPassword}}">
                                            <span class="form-text text-muted">
                                                The database password (usually blank).
                                            </span>
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="step2-dbName" class="col-sm-3 col-form-label">
                                            Database Name
                                        </label>
                                        <div class="col-sm-9">
                                            <input type="text" class="form-control" id="step2-dbName"
                                                placeholder="{{it.defaults.mysqlDatabase}}" value="{{it.defaults.mysqlDatabase}}">
                                            <span class="form-text text-muted">
                                                The name of the database to be used or created. <br>
                                                If left empty, the deployment ID (<code>{{it.deploymentID}}</code>) will be used instead.
                                            </span>
                                        </div>
                                    </div>
                                    <div class="form-group row">
                                        <label for="step2-dbDelete" class="col-sm-3 col-form-label">
                                            Delete Database
                                        </label>
                                        <div class="col-sm-9">
                                            <label class="c-switch c-switch-label c-switch-pill c-switch-success fix-pill-form">
                                                <input class="c-switch-input" type="checkbox" id="step2-dbDelete" checked>
                                                <span class="c-switch-slider" data-checked="On" data-unchecked="Off"></span>
                                            </label>
                                            <span class="form-text text-muted">
                                                If already exists, automatically deletes the database with the name provided above. <br>
                                                <strong>Warning:</strong> all data will be lost.
                                            </span>
                                        </div>
                                    </div>
                                {{/if}}
                                {{@if(it.inputVars.length)}}
                                    <div class="hrsep hrsep-small mb-3">Custom Variables</div>
                                    {{@each(it.inputVars) => inputVar, key}}
                                        <div class="form-group row">
                                            <label for="step2-customVar{{key}}" class="col-sm-3 col-form-label">
                                                {{inputVar.name}}
                                            </label>
                                            <div class="col-sm-9">
                                                <input type="text" class="form-control customRecipeVars" id="step2-customVar{{key}}" 
                                                    data-varName="{{inputVar.name}}" placeholder="{{inputVar.value}}" value="{{inputVar.value}}">
                                            </div>
                                        </div>
                                    {{/each}}
                                {{/if}}
                            </form>
                            <div class="row justify-content-center m-2">
                                <button class="btn btn-outline-danger" type="button" 
                                    onclick="cancelAction()">Cancel and Return to Setup</button>
                                &nbsp; &nbsp;
                                <button class="btn btn-success" type="button" 
                                    onclick="step2Action();">Run Recipe</button>
                            </div>
                        </div>
                        <div class="card-footer text-muted">Step 3: Run Recipe</div>
                        <div class="card-footer text-muted">Step 4: Configure server.cfg</div>

                    {{#elif(it.step == 'run')}}
                        <div class="card-header">Step 1: Review Recipe ✔️</div>
                        <div class="card-header">Step 2: Input Parameters ✔️</div>
                        <div class="card-header font-weight-bold ">Step 3: Run Recipe</div>
                        <div class="card-body">
                            <div class="row mb-1 mt-0 text-center">
                                <div class="col-8 mx-auto card d-block border-primary p-2 mb-1">
                                    Your recipe is being executed, the server will be deployed to: <br>
                                    <code class="inline-code">{{it.deployPath}}</code>
                                </div>
                            </div>
                            <div class="row mb-3">
                                <div class="col-10 mx-auto card d-block p-2 m-0">
                                    <code id="step3Log" class="text-body"><h3 class="text-center">🐌🐌🐌</h3></code>
                                </div>
                            </div>
                            <div class="mx-auto col-8">
                                <div class="progress">
                                    <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" 
                                        aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" 
                                        style="width: 0%" id="step3Progress">0%</div>
                                </div>
                                <span class="text-danger font-weight-bold d-none" id="step3Error"></span>
                            </div>
                            <div class="row justify-content-center m-2">
                                <button class="btn btn-outline-danger d-none" type="button" 
                                    onclick="cancelAction();" id="step3CancelButton">Cancel and Return to Setup</button>
                                <button type="button" class="btn btn-info d-none"
                                    onclick="window.location.reload();" id="step3NextButton">Next</button>
                            </div>
                        </div>
                        <div class="card-footer text-muted">Step 3: Configure server.cfg</div>
        
                    {{#elif(it.step == 'configure')}}
                        <div class="card-header">Step 1: Review Recipe ✔️</div>
                        <div class="card-header">Step 2: Input Parameters ✔️</div>
                        <div class="card-header">Step 3: Run Recipe ✔️</div>
                        <div class="card-header font-weight-bold">Step 4: Configure server.cfg</div>
                        <div class="card-body">
                            <div class="row mb-1 mt-0 text-center">
                                <div class="col-8 mx-auto card d-block border-primary p-2 mb-1">
                                    Configure your <code class="inline-code">server.cfg</code> file to your liking, <br>
                                    then press the <strong>Save & Run Server</strong> button below.
                                </div>
                            </div>
        
                            <textarea id="codeMirrorTarget" style="width: 100%;" class="cms-s-lucario" name="code">{{it.serverCFG}}</textarea>
                            <div class="row justify-content-center m-2">
                                <button class="btn btn-outline-danger" type="button" onclick="cancelAction()">Cancel and Return to Setup</button>
                                &nbsp; &nbsp;
                                <button class="btn btn-success" type="button" onclick="step4Action();">Save & Run Server</button>
                            </div>
                        </div>
        
                    {{#else}}
                        <div class="card-body text-center">
                            Somethign is wrong 🤔
                        </div>
                    {{/if}}
                </div>
            </div>
        </div>

        <!-- Info Row -->
        <div class="row justify-content-center">
            <div class="col-md-4 ">
                <div class="alert alert-secondary text-center small" role="alert">
                    <strong>Server profile:</strong> {{it.serverProfile}} <br>
                    <strong>txAdmin version:</strong> {{it.txAdminVersion}} <br>
                    <strong>FXServer build:</strong> {{it.fxServerVersion}} 
                    <div class="hrsep hrsep-small">//</div>
                    <h5 class="text-muted">
                        <strong>txAdmin</strong> Official Discord: <br>
                        <a href="https://discord.gg/uAmsGa2" target="_blank">
                            <img src="https://discordapp.com/api/guilds/577993482761928734/widget.png?style=shield"></img>
                        </a>
                    </h5>
                </div>

                <div style="text-align: center" class="pb-3">
                    <svg class="c-sidebar-nav-icon d-none" id="darkToggleLight">
                        <use href="img/coreui_icons.svg#cil-sun"></use>
                    </svg>
                    <svg class="c-sidebar-nav-icon" id="darkToggleDark">
                        <use href="img/coreui_icons.svg#cil-moon"></use>
                    </svg>
                </div>
            </div>
        </div>


    </div>
    <!-- CoreUI and necessary plugins-->
    <!-- <script src="js/jquery.min.js"></script> -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" 
        integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" 
        crossorigin="anonymous"></script>
    <script src="js/coreui.bundle.min.js"></script>
    <script src="js/bootstrap-notify.min.js"></script>
    <script src="js/txadmin/base.js"></script>

    <!-- JS -->
    <!-- <script src="js/codeEditor/codemirror.js"></script> -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.61.1/codemirror.min.js" 
        integrity="sha512-ZTpbCvmiv7Zt4rK0ltotRJVRaSBKFQHQTrwfs6DoYlBYzO1MA6Oz2WguC+LkV8pGiHraYLEpo7Paa+hoVbCfKw==" 
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="js/codeEditor/mode/simple.js"></script>
    <script src="js/codeEditor/mode/fivem-cfg.js"></script>
    <script src="js/codeEditor/mode/yaml.js"></script>
    <script>
        //============================================== CodeMirror & Page Setup
        const currentStep = '{{it.step}}';
        const isPlaceholderRecipe = ('{{it.isPlaceholderRecipe}}' === 'true');
        const codeMirrorTarget = document.getElementById("codeMirrorTarget");

        window.onload = function () {
            if(currentStep == 'review' || currentStep == 'configure'){
                window.CMInstance = CodeMirror.fromTextArea(codeMirrorTarget, {
                    mode: (currentStep == 'review')? 'yaml' : 'fivem-cfg',
                    lineNumbers: true,
                    lineWrapping: true,
                    viewportMargin: Infinity,
                    theme: "lucario" //NOTE: I modified the theme a bit
                });
            }
        };


        //============================================== Step1 action
        function step1Action(){
            const editor = window.CMInstance;
            if(isPlaceholderRecipe && codeMirrorTarget.value == editor.getValue()){
                return $.notify({ message: `If you are not trying to write a recipe, click on the cancel button below to return to the setup page, where you can select a recommended template.` }, { type: 'warning' });
            }
            editor.setValue(editor.getValue().replace(/\t/g, ' '.repeat(4)));
            const recipe = editor.getValue();
            if(recipe.length < 128){
                return $.notify({ message: `Your Recipe file is too small, there is a good chance you deleted something you shouldn't.` }, { type: 'warning' });
            }
            const notify = $.notify({ message: '<p class="text-center">Saving...</p>' }, {});

            txAdminAPI({
                type: "POST",
                url: '/deployer/recipe/confirmRecipe',
                timeout: REQ_TIMEOUT_LONG,
                data: {recipe},
                dataType: 'json',
                success: function (data) {
                    if(checkDoLogoutRefresh(data)) return;
                    if (data.success == true){
                        window.location.reload(true);
                    }else{
                        notify.update('progress', 0);
                        notify.update('type', data.type);
                        notify.update('message', data.message);
                    }
                    
                },
                error: function (xmlhttprequest, textstatus, message) {
                    notify.update('progress', 0);
                    notify.update('type', 'danger');
                    notify.update('message', message);
                }
            });
        }

        //============================================== Step2 action
        const requireDBConfig = ('{{it.requireDBConfig}}' == 'true');
        const step2elements = {
            form: document.getElementById('step2-form'),
            svLicense: document.getElementById('step2-svLicense'),
        }
        if(requireDBConfig){
            step2elements.dbHost = document.getElementById('step2-dbHost');
            step2elements.dbUsername = document.getElementById('step2-dbUsername');
            step2elements.dbPassword = document.getElementById('step2-dbPassword');
            step2elements.dbName = document.getElementById('step2-dbName');
            step2elements.dbDelete = document.getElementById('step2-dbDelete');

            step2elements.dbName.addEventListener("keyup", () => {
                const dbName = step2elements.dbName.value.trim()
                step2elements.dbDelete.checked = (dbName == step2elements.dbName.placeholder || !dbName.length);
            });
        }
        

        function step2Action(){
            if(!step2elements.form.reportValidity()) return;
            const postData = {
                svLicense: step2elements.svLicense.value.trim()
            }
            if(requireDBConfig){
                postData.dbHost = step2elements.dbHost.value.trim();
                postData.dbUsername = step2elements.dbUsername.value.trim();
                postData.dbPassword = step2elements.dbPassword.value.trim();
                postData.dbName = step2elements.dbName.value.trim();
                postData.dbDelete = step2elements.dbDelete.checked;

                if(!postData.dbName.length){
                    postData.dbName = step2elements.dbName.placeholder;
                }
                if(!postData.dbHost || !postData.dbUsername){
                    return $.notify({ message: `The database host and username are required.` }, { type: 'warning' });
                }
            }

            //Getting custom forms:
            const customVarInputs = step2elements.form.getElementsByClassName("customRecipeVars");
            for(const input of customVarInputs){
                if(!input.dataset.varname) continue;
                postData[input.dataset.varname] = input.value.trim();
            }

            const notify = $.notify({ message: '<p class="text-center">Saving...</p>' }, {});
            txAdminAPI({
                type: "POST",
                url: '/deployer/recipe/setVariables',
                timeout: REQ_TIMEOUT_LONG,
                data: postData,
                dataType: 'json',
                success: function (data) {
                    if(checkDoLogoutRefresh(data)) return;
                    if (data.success == true){
                        window.location.reload(true);
                    }else{
                        notify.update('progress', 0);
                        notify.update('type', data.type);
                        notify.update('message', data.message);
                    }
                    
                },
                error: function (xmlhttprequest, textstatus, message) {
                    notify.update('progress', 0);
                    notify.update('type', 'danger');
                    notify.update('message', message);
                }
            });
        }

        //============================================== Step3 action
        const step3elements = {
            log: document.getElementById('step3Log'),
            progress: document.getElementById('step3Progress'),
            error: document.getElementById('step3Error'),
            cancelButton: document.getElementById('step3CancelButton'),
            nextButton: document.getElementById('step3NextButton'),
        }
        const refreshStatus = async () => {
            txAdminAPI({
                type: "GET",
                url: '/deployer/status',
                timeout: REQ_TIMEOUT_LONG,
                success: function (data) {
                    if(checkDoLogoutRefresh(data)) return;
                    step3elements.error.classList.add('d-none');
                    step3elements.log.innerText = data.log;
                    
                    if(data.status == 'done'){
                        step3elements.progress.classList.remove('progress-bar-animated', 'progress-bar-striped');
                        step3elements.progress.classList.add('bg-success');
                        step3elements.nextButton.classList.remove('d-none');
                        step3elements.progress.ariaValueNow = 100;
                        step3elements.progress.style.width = '100%';
                        step3elements.progress.innerText = 'DONE';
                        
                    }else if(data.status == 'failed'){
                        step3elements.progress.classList.remove('progress-bar-animated', 'progress-bar-striped');
                        step3elements.progress.classList.add('bg-danger');
                        step3elements.cancelButton.classList.remove('d-none');
                        step3elements.progress.ariaValueNow = 100;
                        step3elements.progress.style.width = '100%';
                        step3elements.progress.innerText = 'FAILED';

                    }else{
                        const ariaMin = (data.progress > 5)? data.progress : 5;
                        step3elements.progress.ariaValueNow = ariaMin;
                        step3elements.progress.style.width = ariaMin + '%';
                        step3elements.progress.innerText = data.progress + '%';
                        step3elements.progress.scrollIntoView();

                    }
                },
                error: function (xmlhttprequest, textstatus, message) {
                    step3elements.error.innerText = `Error: ${textstatus}`;
                    step3elements.error.classList.remove('d-none');
                }
            });
        }

        if(currentStep == 'run') setInterval(refreshStatus, 1000);


        //============================================== Step4 action
        function step4Action(){
            const serverCFG = window.CMInstance.getValue();
            if(serverCFG.length < 256){
                return $.notify({ message: `Your settings.cfg file is too small, there is a good chance it is not valid.` }, { type: 'warning' });
            }
            const notify = $.notify({ message: '<p class="text-center">Saving...</p>' }, {});

            if(serverCFG.includes(`sv_licenseKey "changeme"`)){
                return $.notify({ message: `Please change the <strong>sv_licenseKey</strong>.` }, { type: 'danger' });
            }

            txAdminAPI({
                type: "POST",
                url: '/deployer/recipe/commit',
                timeout: REQ_TIMEOUT_LONG,
                data: {serverCFG},
                dataType: 'json',
                success: function (data) {
                    if(checkDoLogoutRefresh(data)) return;
                    if (data.success == true){
                        window.location = '/console';
                    }else{
                        notify.update('progress', 0);
                        notify.update('type', data.type);
                        notify.update('message', data.message);
                    }
                },
                error: function (xmlhttprequest, textstatus, message) {
                    notify.update('progress', 0);
                    notify.update('type', 'danger');
                    notify.update('message', message);
                }
            });
        }

        //============================================== cancel action
        function cancelAction(){
            const notify = $.notify({ message: '<p class="text-center">Cancelling...</p>' }, {});
            txAdminAPI({
                type: "POST",
                url: '/deployer/recipe/cancel',
                timeout: REQ_TIMEOUT_LONG,
                success: function (data) {
                    if(checkDoLogoutRefresh(data)) return;
                    if (data.success == true){
                        window.location = '/setup';
                    }else{
                        notify.update('progress', 0);
                        notify.update('type', data.type);
                        notify.update('message', data.message);
                    }
                },
                error: function (xmlhttprequest, textstatus, message) {
                    notify.update('progress', 0);
                    notify.update('type', 'danger');
                    notify.update('message', message);
                }
            });
        }
    </script>

</body>

</html>
