Merge branch 'MHSanaei:main' into main

This commit is contained in:
sepidezare 2023-05-01 01:29:07 +03:30 committed by GitHub
commit f038b681e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 131 additions and 193 deletions

View file

@ -177,6 +177,8 @@ Reference syntax:
| `POST` | `"/resetAllClientTraffics/:id"` | Reset traffics of all clients in an inbound |
| `POST` | `"/delDepletedClients/:id"` | Delete inbound depleted clients (-1: all) |
- [Postman Collection](https://gist.github.com/mehdikhody/9a862801a2e41f6b5fb6bbc7e1326044)
# A Special Thanks To
- [alireza0](https://github.com/alireza0/)
@ -190,11 +192,8 @@ Reference syntax:
# Buy Me a Coffee
[![](https://img.shields.io/badge/Wallet-USDT__TRC20-green.svg)](#)
- Tron USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
```
TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC
```
# Pictures

View file

@ -73,7 +73,7 @@ config_after_install() {
/usr/local/x-ui/x-ui migrate
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
if [[ x"${config_confirm}" == x"y" || x"${config_confirm}" == x"Y" ]]; then
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
read -p "Please set up your username:" config_account
echo -e "${yellow}Your username will be:${config_account}${plain}"
read -p "Please set up your password:" config_password

View file

@ -212,6 +212,7 @@
.ant-card-dark .ant-modal-close,
.ant-card-dark i,
.ant-card-dark .ant-select-dropdown-menu-item,
.ant-card-dark .ant-calendar-day-select,
.ant-card-dark .ant-calendar-month-select,
.ant-card-dark .ant-calendar-year-select,
.ant-card-dark .ant-calendar-date,
@ -226,7 +227,7 @@
.ant-card-dark .ant-calendar-date:hover,
.ant-card-dark .ant-select-dropdown-menu-item-active,
.ant-card-dark li.ant-calendar-time-picker-select-option-selected {
background-color: #004488;
background-color: #11314d;
}
.ant-card-dark tbody .ant-table-expanded-row,
@ -243,7 +244,7 @@
.ant-card-dark .ant-select-selection,
.ant-card-dark .ant-calendar-picker-clear {
color: hsla(0,0%,100%,.65);
background-color: #2e3b52;
background-color: #193752;
}
.ant-card-dark .ant-select-disabled .ant-select-selection {
@ -264,12 +265,19 @@
.ant-card-dark .ant-modal-content,
.ant-card-dark .ant-modal-body,
.ant-card-dark .ant-modal-header,
.ant-card-dark .ant-calendar-selected-day .ant-calendar-date {
.ant-card-dark .ant-modal-header {
color: hsla(0,0%,100%,.65);
background-color: #222a37;
}
.ant-card-dark .ant-calendar-selected-day .ant-calendar-date {
background-color: #1668dc;
}
.ant-card-dark .ant-calendar-time-picker-select li:hover {
background: #1668dc;
}
.client-table-header {
background-color: #f0f2f5;
}

View file

@ -79,9 +79,15 @@ const UTLS_FINGERPRINT = {
};
const ALPN_OPTION = {
H3: "h3",
H2: "h2",
HTTP1: "http/1.1",
H2: "h2",
H3: "h3",
};
const SNIFFING_OPTION = {
HTTP: "http",
TLS: "tls",
QUIC: "quic",
};
Object.freeze(Protocols);
@ -92,6 +98,7 @@ Object.freeze(TLS_FLOW_CONTROL);
Object.freeze(TLS_VERSION_OPTION);
Object.freeze(TLS_CIPHER_OPTION);
Object.freeze(ALPN_OPTION);
Object.freeze(SNIFFING_OPTION);
class XrayCommonClass {
@ -880,7 +887,7 @@ class StreamSettings extends XrayCommonClass {
}
class Sniffing extends XrayCommonClass {
constructor(enabled=true, destOverride=['http', 'tls']) {
constructor(enabled=true, destOverride=['http', 'tls', 'quic']) {
super();
this.enabled = enabled;
this.destOverride = destOverride;
@ -890,7 +897,7 @@ class Sniffing extends XrayCommonClass {
let destOverride = ObjectUtil.clone(json.destOverride);
if (!ObjectUtil.isEmpty(destOverride) && !ObjectUtil.isArrEmpty(destOverride)) {
if (ObjectUtil.isEmpty(destOverride[0])) {
destOverride = ['http', 'tls'];
destOverride = ['http', 'tls', 'quic'];
}
}
return new Sniffing(

View file

@ -1,9 +1,10 @@
package controller
import (
"github.com/gin-gonic/gin"
"net/http"
"x-ui/web/session"
"github.com/gin-gonic/gin"
)
type BaseController struct {

View file

@ -7,7 +7,8 @@
<link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue@1.7.2/antd.min.css">
<link rel="stylesheet" href="{{ .base_path }}assets/element-ui@2.15.0/theme-chalk/display.css">
<link rel="stylesheet" href="{{ .base_path }}assets/css/custom.css?{{ .cur_ver }}">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel=”icon” type=”image/x-icon” href="{{ .base_path }}assets/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="{{ .base_path }}assets/favicon.ico">
<style>
[v-cloak] {
display: none;

View file

@ -91,7 +91,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker>
</a-form-item>

View file

@ -120,7 +120,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
<a-tag color="red" v-if="isExpiry">Expired</a-tag>

View file

@ -49,7 +49,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="dbInbound._expiryTime" style="width: 300px;"></a-date-picker>
</a-form-item>

View file

@ -65,7 +65,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
</a-form-item>

View file

@ -71,7 +71,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
</a-form-item>

View file

@ -62,7 +62,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
</a-form-item>

View file

@ -12,5 +12,10 @@
</span>
<a-switch v-model="inbound.sniffing.enabled"></a-switch>
</a-form-item>
<a-form-item>
<a-checkbox-group v-model="inbound.sniffing.destOverride" v-if="inbound.sniffing.enabled">
<a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key">[[ value ]]</a-checkbox>
</a-checkbox-group>
</a-form-item>
</a-form>
{{end}}

View file

@ -37,7 +37,7 @@
<a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
</a-form-item>
<a-form-item label="CipherSuites">
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px">
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<a-select-option value="">auto</a-select-option>
<a-select-option v-for="key in TLS_CIPHER_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select>
@ -56,14 +56,15 @@
<a-input v-model.trim="inbound.stream.tls.settings.serverName" style="width: 250px"></a-input>
</a-form-item>
<a-form-item label="uTLS">
<a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 170px">
<a-select v-model="inbound.stream.tls.settings.fingerprint"
style="width: 170px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<a-select-option value=''>None</a-select-option>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="Alpn">
<a-checkbox-group v-model="inbound.stream.tls.alpn" style="width:200px">
<a-checkbox v-for="key in ALPN_OPTION" :value="key">[[ key ]]</a-checkbox>
<a-checkbox v-for="key,value in ALPN_OPTION" :value="key">[[ value ]]</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="Allow insecure">
@ -82,7 +83,7 @@
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
<a-input v-model.trim="inbound.stream.tls.certs[0].keyFile" style="width:300px;"></a-input>
</a-form-item>
<a-button @click="setDefaultCertData">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
<a-button type="primary" icon="import" @click="setDefaultCertData">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
</template>
<template v-else>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
@ -120,7 +121,7 @@
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
<a-input v-model.trim="inbound.stream.xtls.certs[0].keyFile" style="width:300px;"></a-input>
</a-form-item>
<a-button @click="setDefaultCertData">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
<a-button type="primary" icon="import" @click="setDefaultCertData">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
</template>
<template v-else>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
@ -142,7 +143,8 @@
<a-input type="number" v-model.number="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input>
</a-form-item>
<a-form-item label="uTLS" >
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 135px">
<a-select v-model="inbound.stream.reality.settings.fingerprint"
style="width: 135px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>

View file

@ -66,28 +66,42 @@
<transition name="list" appear>
<a-card hoverable :class="siderDrawer.isDarkTheme ? darkClass : ''">
<div slot="title">
<a-button type="primary" icon="plus" @click="openAddInbound">{{ i18n "pages.inbounds.addInbound" }}</a-button>
<a-dropdown :trigger="['click']">
<a-button type="primary" icon="menu">{{ i18n "pages.inbounds.generalActions" }}</a-button>
<a-menu slot="overlay" @click="a => generalActions(a)" :theme="siderDrawer.theme">
<a-menu-item key="export">
<a-icon type="export"></a-icon>
{{ i18n "pages.inbounds.export" }}
</a-menu-item>
<a-menu-item key="resetInbounds">
<a-icon type="reload"></a-icon>
{{ i18n "pages.inbounds.resetAllTraffic" }}
</a-menu-item>
<a-menu-item key="resetClients">
<a-icon type="file-done"></a-icon>
{{ i18n "pages.inbounds.resetAllClientTraffics" }}
</a-menu-item>
<a-menu-item key="delDepletedClients">
<a-icon type="rest"></a-icon>
{{ i18n "pages.inbounds.delDepletedClients" }}
</a-menu-item>
</a-menu>
</a-dropdown>
<a-row>
<a-col :xs="24" :sm="24" :lg="12">
<a-button type="primary" icon="plus" @click="openAddInbound">{{ i18n "pages.inbounds.addInbound" }}</a-button>
<a-dropdown :trigger="['click']">
<a-button type="primary" icon="menu">{{ i18n "pages.inbounds.generalActions" }}</a-button>
<a-menu slot="overlay" @click="a => generalActions(a)" :theme="siderDrawer.theme">
<a-menu-item key="export">
<a-icon type="export"></a-icon>
{{ i18n "pages.inbounds.export" }}
</a-menu-item>
<a-menu-item key="resetInbounds">
<a-icon type="reload"></a-icon>
{{ i18n "pages.inbounds.resetAllTraffic" }}
</a-menu-item>
<a-menu-item key="resetClients">
<a-icon type="file-done"></a-icon>
{{ i18n "pages.inbounds.resetAllClientTraffics" }}
</a-menu-item>
<a-menu-item key="delDepletedClients">
<a-icon type="rest"></a-icon>
{{ i18n "pages.inbounds.delDepletedClients" }}
</a-menu-item>
</a-menu>
</a-dropdown>
</a-col>
<a-col :xs="24" :sm="24" :lg="12" style="text-align: right;">
<a-select v-model="refreshInterval"
v-if="isRefreshEnabled"
@change="changeRefreshInterval"
:dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
</a-select>
<a-icon type="sync" :spin="isRefreshEnabled"></a-icon>
<a-switch v-model="isRefreshEnabled" @change="toggleRefresh"></a-switch>
</a-col>
</a-row>
</div>
<a-input v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px"></a-input>
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
@ -263,7 +277,7 @@
}, {
title: '{{ i18n "pages.inbounds.protocol" }}',
align: 'left',
width: 80,
width: 90,
scopedSlots: { customRender: 'protocol' },
}, {
title: '{{ i18n "clients" }}',
@ -315,25 +329,22 @@
defaultCert: '',
defaultKey: '',
clientCount: {},
isRefreshEnabled: localStorage.getItem("isRefreshEnabled") === "true" ? true : false,
refreshInterval: Number(localStorage.getItem("refreshInterval")) || 5000,
},
methods: {
loading(spinning=true) {
this.spinning = spinning;
},
async getDBInbounds() {
this.loading();
const msg = await HttpUtil.post('/xui/inbound/list');
this.loading(false);
if (!msg.success) {
return;
}
this.setInbounds(msg.obj);
this.searchKey = '';
},
async getDefaultSettings() {
this.loading();
const msg = await HttpUtil.post('/xui/setting/defaultSettings');
this.loading(false);
if (!msg.success) {
return;
}
@ -345,17 +356,16 @@
setInbounds(dbInbounds) {
this.inbounds.splice(0);
this.dbInbounds.splice(0);
this.searchedInbounds.splice(0);
for (const inbound of dbInbounds) {
const dbInbound = new DBInbound(inbound);
to_inbound = dbInbound.toInbound()
this.inbounds.push(to_inbound);
this.dbInbounds.push(dbInbound);
this.searchedInbounds.push(dbInbound);
if([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN].includes(inbound.protocol) ){
this.clientCount[inbound.id] = this.getClientCounts(inbound,to_inbound);
}
}
this.searchInbounds(this.searchKey);
},
getClientCounts(dbInbound,inbound){
let clientCount = 0,active = [], deactive = [], depleted = [], expiring = [];
@ -788,6 +798,25 @@
}
txtModal.show('{{ i18n "pages.inbounds.export"}}',copyText,'All-Inbounds');
},
async startDataRefreshLoop() {
while (this.isRefreshEnabled) {
try {
await this.getDBInbounds();
} catch (e) {
console.error(e);
}
await PromiseUtil.sleep(this.refreshInterval);
}
},
toggleRefresh() {
localStorage.setItem("isRefreshEnabled", this.isRefreshEnabled);
if (this.isRefreshEnabled) {
this.startDataRefreshLoop();
}
},
changeRefreshInterval(){
localStorage.setItem("refreshInterval", this.refreshInterval);
},
},
watch: {
searchKey: debounce(function (newVal) {
@ -795,8 +824,15 @@
}, 500)
},
mounted() {
this.loading();
this.getDefaultSettings();
this.getDBInbounds();
if (this.isRefreshEnabled) {
this.startDataRefreshLoop();
}
else {
this.getDBInbounds();
}
this.loading(false);
},
computed: {
total() {
@ -823,7 +859,6 @@
}
},
});
</script>
{{template "inboundModal"}}

View file

@ -830,12 +830,14 @@ func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.Cl
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
logger.Warning(err)
return nil, err
}
logger.Warning(err)
return nil, err
}
return traffics[0], err
if len(traffics) > 0 {
return traffics[0], nil
}
return nil, nil
}
func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error) {

View file

@ -33,9 +33,6 @@ import (
//go:embed assets/*
var assetsFS embed.FS
//go:embed assets/favicon.ico
var favicon []byte
//go:embed html/*
var htmlFS embed.FS
@ -161,11 +158,6 @@ func (s *Server) initRouter() (*gin.Engine, error) {
engine := gin.Default()
// Add favicon
engine.GET("/favicon.ico", func(c *gin.Context) {
c.Data(200, "image/x-icon", favicon)
})
secret, err := s.settingService.GetSecret()
if err != nil {
return nil, err

128
x-ui.sh
View file

@ -59,13 +59,13 @@ fi
confirm() {
if [[ $# > 1 ]]; then
echo && read -p "$1 [Default $2]: " temp
if [[ x"${temp}" == x"" ]]; then
if [[ "${temp}" == "" ]]; then
temp=$2
fi
else
read -p "$1 [y/n]: " temp
fi
if [[ x"${temp}" == x"y" || x"${temp}" == x"Y" ]]; then
if [[ "${temp}" == "y" || "${temp}" == "Y" ]]; then
return 0
else
return 1
@ -342,7 +342,7 @@ check_status() {
return 2
fi
temp=$(systemctl status x-ui | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1)
if [[ x"${temp}" == x"running" ]]; then
if [[ "${temp}" == "running" ]]; then
return 0
else
return 1
@ -351,7 +351,7 @@ check_status() {
check_enabled() {
temp=$(systemctl is-enabled x-ui)
if [[ x"${temp}" == x"enabled" ]]; then
if [[ "${temp}" == "enabled" ]]; then
return 0
else
return 1
@ -431,32 +431,6 @@ show_xray_status() {
fi
}
#this will be an entrance for ssl cert issue
#here we can provide two different methods to issue cert
#first.standalone mode second.DNS API mode
ssl_cert_issue() {
local method=""
echo -E ""
LOGD "********Usage********"
LOGI "this shell script will use acme to help issue certs."
LOGI "here we provide two methods for issuing certs:"
LOGI "method 1:acme standalone mode,need to keep port:80 open"
LOGI "method 2:acme DNS API mode,need provide Cloudflare Global API Key"
LOGI "recommend method 2 first,if it fails,you can try method 1."
LOGI "certs will be installed in /root/cert directory"
read -p "please choose which method do you want,type 1 or 2": method
LOGI "you choosed method:${method}"
if [ "${method}" == "1" ]; then
ssl_cert_issue_standalone
elif [ "${method}" == "2" ]; then
ssl_cert_issue_by_cloudflare
else
LOGE "invalid input,please check it..."
exit 1
fi
}
open_ports() {
if ! command -v ufw &> /dev/null
then
@ -544,7 +518,7 @@ install_acme() {
}
#method for standalone mode
ssl_cert_issue_standalone() {
ssl_cert_issue() {
#check for acme.sh first
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
echo "acme.sh could not be found. we will install it"
@ -555,7 +529,7 @@ ssl_cert_issue_standalone() {
fi
fi
#install socat second
if [[ x"${release}" == x"centos" ]]; then
if [[ "${release}" == "centos" ]] || [[ "${release}" == "fedora" ]] ; then
yum install socat -y
else
apt install socat -y
@ -569,7 +543,7 @@ ssl_cert_issue_standalone() {
#get the domain here,and we need verify it
local domain=""
read -p "please input your domain:" domain
read -p "Please enter your domain name:" domain
LOGD "your domain is:${domain},check it..."
#here we need to judge whether there exists cert already
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
@ -636,94 +610,6 @@ ssl_cert_issue_standalone() {
}
#method for DNS API mode
ssl_cert_issue_by_cloudflare() {
echo -E ""
LOGD "******Preconditions******"
LOGI "1.need Cloudflare account associated email"
LOGI "2.need Cloudflare Global API Key"
LOGI "3.your domain use Cloudflare as resolver"
confirm "I have confirmed all these info above[y/n]" "y"
if [ $? -eq 0 ]; then
install_acme
if [ $? -ne 0 ]; then
LOGE "install acme failed,please check logs"
exit 1
fi
CF_Domain=""
CF_GlobalKey=""
CF_AccountEmail=""
LOGD "please input your domain:"
read -p "Input your domain here:" CF_Domain
LOGD "your domain is:${CF_Domain},check it..."
#here we need to judge whether there exists cert already
local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}')
if [ ${currentCert} == ${CF_Domain} ]; then
local certInfo=$(~/.acme.sh/acme.sh --list)
LOGE "system already have certs here,can not issue again,current certs details:"
LOGI "$certInfo"
exit 1
else
LOGI "your domain is ready for issuing cert now..."
fi
#create a directory for install cert
certPath="/root/cert/${CF_Domain}"
if [ ! -d "$certPath" ]; then
mkdir -p "$certPath"
else
rm -rf "$certPath"
mkdir -p "$certPath"
fi
LOGD "please inout your cloudflare global API key:"
read -p "Input your key here:" CF_GlobalKey
LOGD "your cloudflare global API key is:${CF_GlobalKey}"
LOGD "please input your cloudflare account email:"
read -p "Input your email here:" CF_AccountEmail
LOGD "your cloudflare account email:${CF_AccountEmail}"
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
if [ $? -ne 0 ]; then
LOGE "change the default CA to Lets'Encrypt failed,exit"
exit 1
fi
export CF_Key="${CF_GlobalKey}"
export CF_Email=${CF_AccountEmail}
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log
if [ $? -ne 0 ]; then
LOGE "issue cert failed,exit"
rm -rf ~/.acme.sh/${CF_Domain}
exit 1
else
LOGI "Certificate issued Successfully, Installing..."
fi
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \
--key-file /root/cert/${CF_Domain}/privkey.pem \
--fullchain-file /root/cert/${CF_Domain}/fullchain.pem
if [ $? -ne 0 ]; then
LOGE "install cert failed,exit"
rm -rf ~/.acme.sh/${CF_Domain}
exit 1
else
LOGI "Certificate installed Successfully,Turning on automatic updates..."
fi
~/.acme.sh/acme.sh --upgrade --auto-upgrade
if [ $? -ne 0 ]; then
LOGE "auto renew failed, certs details:"
ls -lah cert/*
chmod 755 $certPath/*
exit 1
else
LOGI "auto renew succeed, certs details:"
ls -lah cert/*
chmod 755 $certPath/*
fi
else
show_menu
fi
}
warp_fixchatgpt() {
curl -fsSL https://gist.githubusercontent.com/hamid-gh98/dc5dd9b0cc5b0412af927b1ccdb294c7/raw/install_warp_proxy.sh | bash