Оптимизация
This commit is contained in:
@@ -6,11 +6,12 @@ import certsData from './mock-data/certs.json'
|
|||||||
import vaccessData from './mock-data/vaccess.json'
|
import vaccessData from './mock-data/vaccess.json'
|
||||||
|
|
||||||
const delay = (ms = 300) => new Promise(r => setTimeout(r, ms))
|
const delay = (ms = 300) => new Promise(r => setTimeout(r, ms))
|
||||||
|
const clone = (data) => JSON.parse(JSON.stringify(data))
|
||||||
|
|
||||||
export const mockApi = {
|
export const mockApi = {
|
||||||
async getAllServicesStatus() {
|
async getAllServicesStatus() {
|
||||||
await delay(200)
|
await delay(200)
|
||||||
return JSON.parse(JSON.stringify(servicesData))
|
return clone(servicesData)
|
||||||
},
|
},
|
||||||
|
|
||||||
async checkServicesReady() {
|
async checkServicesReady() {
|
||||||
@@ -36,7 +37,7 @@ export const mockApi = {
|
|||||||
|
|
||||||
async getSitesList() {
|
async getSitesList() {
|
||||||
await delay(200)
|
await delay(200)
|
||||||
return JSON.parse(JSON.stringify(sitesData))
|
return clone(sitesData)
|
||||||
},
|
},
|
||||||
|
|
||||||
async createNewSite(siteJSON) {
|
async createNewSite(siteJSON) {
|
||||||
@@ -65,12 +66,12 @@ export const mockApi = {
|
|||||||
|
|
||||||
async getProxyList() {
|
async getProxyList() {
|
||||||
await delay(200)
|
await delay(200)
|
||||||
return JSON.parse(JSON.stringify(proxiesData))
|
return clone(proxiesData)
|
||||||
},
|
},
|
||||||
|
|
||||||
async getVAccessRules(host, isProxy) {
|
async getVAccessRules(host, isProxy) {
|
||||||
await delay(200)
|
await delay(200)
|
||||||
return JSON.parse(JSON.stringify(vaccessData))
|
return clone(vaccessData)
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveVAccessRules(host, isProxy, configJSON) {
|
async saveVAccessRules(host, isProxy, configJSON) {
|
||||||
@@ -80,7 +81,7 @@ export const mockApi = {
|
|||||||
|
|
||||||
async getConfig() {
|
async getConfig() {
|
||||||
await delay(200)
|
await delay(200)
|
||||||
return JSON.parse(JSON.stringify(configData))
|
return clone(configData)
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveConfig(configJSON) {
|
async saveConfig(configJSON) {
|
||||||
@@ -111,7 +112,7 @@ export const mockApi = {
|
|||||||
|
|
||||||
async getAllCertsInfo() {
|
async getAllCertsInfo() {
|
||||||
await delay(300)
|
await delay(300)
|
||||||
return JSON.parse(JSON.stringify(certsData))
|
return clone(certsData)
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteCertificate(domain) {
|
async deleteCertificate(domain) {
|
||||||
|
|||||||
@@ -1,99 +1,26 @@
|
|||||||
const app = () => window.go.admin.App
|
const app = () => window.go.admin.App
|
||||||
|
|
||||||
export const wailsApi = {
|
const methods = [
|
||||||
async getAllServicesStatus() {
|
'GetAllServicesStatus', 'CheckServicesReady',
|
||||||
return await app().GetAllServicesStatus()
|
'StartServer', 'StopServer',
|
||||||
},
|
'StartHTTPService', 'StopHTTPService',
|
||||||
|
'StartHTTPSService', 'StopHTTPSService',
|
||||||
|
'StartMySQLService', 'StopMySQLService',
|
||||||
|
'StartPHPService', 'StopPHPService',
|
||||||
|
'EnableProxyService', 'DisableProxyService',
|
||||||
|
'EnableACMEService', 'DisableACMEService',
|
||||||
|
'RestartAllServices',
|
||||||
|
'GetSitesList', 'CreateNewSite', 'DeleteSite', 'UpdateSiteCache', 'OpenSiteFolder',
|
||||||
|
'UploadCertificate',
|
||||||
|
'GetProxyList',
|
||||||
|
'GetVAccessRules', 'SaveVAccessRules',
|
||||||
|
'GetConfig', 'SaveConfig', 'ReloadConfig',
|
||||||
|
'ObtainSSLCertificate', 'ObtainAllSSLCertificates',
|
||||||
|
'GetCertInfo', 'GetAllCertsInfo', 'DeleteCertificate', 'ReloadSSLCertificates',
|
||||||
|
]
|
||||||
|
|
||||||
async checkServicesReady() {
|
const toCamelCase = (s) => s.charAt(0).toLowerCase() + s.slice(1)
|
||||||
return await app().CheckServicesReady()
|
|
||||||
},
|
|
||||||
|
|
||||||
async startServer() { return await app().StartServer() },
|
export const wailsApi = Object.fromEntries(
|
||||||
async stopServer() { return await app().StopServer() },
|
methods.map(m => [toCamelCase(m), (...args) => app()[m](...args)])
|
||||||
async startHTTPService() { return await app().StartHTTPService() },
|
)
|
||||||
async stopHTTPService() { return await app().StopHTTPService() },
|
|
||||||
async startHTTPSService() { return await app().StartHTTPSService() },
|
|
||||||
async stopHTTPSService() { return await app().StopHTTPSService() },
|
|
||||||
async startMySQLService() { return await app().StartMySQLService() },
|
|
||||||
async stopMySQLService() { return await app().StopMySQLService() },
|
|
||||||
async startPHPService() { return await app().StartPHPService() },
|
|
||||||
async stopPHPService() { return await app().StopPHPService() },
|
|
||||||
async enableProxyService() { return await app().EnableProxyService() },
|
|
||||||
async disableProxyService() { return await app().DisableProxyService() },
|
|
||||||
async enableACMEService() { return await app().EnableACMEService() },
|
|
||||||
async disableACMEService() { return await app().DisableACMEService() },
|
|
||||||
async restartAllServices() { return await app().RestartAllServices() },
|
|
||||||
|
|
||||||
async getSitesList() {
|
|
||||||
return await app().GetSitesList()
|
|
||||||
},
|
|
||||||
|
|
||||||
async createNewSite(siteJSON) {
|
|
||||||
return await app().CreateNewSite(siteJSON)
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteSite(host) {
|
|
||||||
return await app().DeleteSite(host)
|
|
||||||
},
|
|
||||||
|
|
||||||
async updateSiteCache() {
|
|
||||||
return await app().UpdateSiteCache()
|
|
||||||
},
|
|
||||||
|
|
||||||
async openSiteFolder(host) {
|
|
||||||
return await app().OpenSiteFolder(host)
|
|
||||||
},
|
|
||||||
|
|
||||||
async uploadCertificate(host, certType, certDataBase64) {
|
|
||||||
return await app().UploadCertificate(host, certType, certDataBase64)
|
|
||||||
},
|
|
||||||
|
|
||||||
async getProxyList() {
|
|
||||||
return await app().GetProxyList()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getVAccessRules(host, isProxy) {
|
|
||||||
return await app().GetVAccessRules(host, isProxy)
|
|
||||||
},
|
|
||||||
|
|
||||||
async saveVAccessRules(host, isProxy, configJSON) {
|
|
||||||
return await app().SaveVAccessRules(host, isProxy, configJSON)
|
|
||||||
},
|
|
||||||
|
|
||||||
async getConfig() {
|
|
||||||
return await app().GetConfig()
|
|
||||||
},
|
|
||||||
|
|
||||||
async saveConfig(configJSON) {
|
|
||||||
return await app().SaveConfig(configJSON)
|
|
||||||
},
|
|
||||||
|
|
||||||
async reloadConfig() {
|
|
||||||
return await app().ReloadConfig()
|
|
||||||
},
|
|
||||||
|
|
||||||
async obtainSSLCertificate(domain) {
|
|
||||||
return await app().ObtainSSLCertificate(domain)
|
|
||||||
},
|
|
||||||
|
|
||||||
async obtainAllSSLCertificates() {
|
|
||||||
return await app().ObtainAllSSLCertificates()
|
|
||||||
},
|
|
||||||
|
|
||||||
async getCertInfo(domain) {
|
|
||||||
return await app().GetCertInfo(domain)
|
|
||||||
},
|
|
||||||
|
|
||||||
async getAllCertsInfo() {
|
|
||||||
return await app().GetAllCertsInfo()
|
|
||||||
},
|
|
||||||
|
|
||||||
async deleteCertificate(domain) {
|
|
||||||
return await app().DeleteCertificate(domain)
|
|
||||||
},
|
|
||||||
|
|
||||||
async reloadSSLCertificates() {
|
|
||||||
return await app().ReloadSSLCertificates()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|||||||
35
front_vue/src/Core/composables/useCertLookup.js
Normal file
35
front_vue/src/Core/composables/useCertLookup.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
export function useCertLookup() {
|
||||||
|
const certsStore = useCertsStore()
|
||||||
|
|
||||||
|
const findCertForDomain = (domain, aliases = []) => {
|
||||||
|
const allDomains = [domain, ...aliases.filter(a => !a.includes('*'))]
|
||||||
|
|
||||||
|
for (const d of allDomains) {
|
||||||
|
const direct = certsStore.list.find(c => c.domain === d && c.has_cert)
|
||||||
|
if (direct) return direct
|
||||||
|
|
||||||
|
const parts = d.split('.')
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
const wildcard = '*.' + parts.slice(1).join('.')
|
||||||
|
const wc = certsStore.list.find(c => c.domain === wildcard && c.has_cert)
|
||||||
|
if (wc) return wc
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cert of certsStore.list) {
|
||||||
|
if (cert.has_cert && cert.dns_names) {
|
||||||
|
for (const dns of cert.dns_names) {
|
||||||
|
if (dns === d) return cert
|
||||||
|
if (dns.startsWith('*.')) {
|
||||||
|
const base = dns.slice(2)
|
||||||
|
const dParts = d.split('.')
|
||||||
|
if (dParts.length >= 2 && dParts.slice(1).join('.') === base) return cert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return { findCertForDomain }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export function useDraggable(list) {
|
export function useDraggable(list, { onReorder } = {}) {
|
||||||
const dragIndex = ref(null)
|
const dragIndex = ref(null)
|
||||||
const dragOverIndex = ref(null)
|
const dragOverIndex = ref(null)
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export function useDraggable(list) {
|
|||||||
dragOverIndex.value = null
|
dragOverIndex.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDrop = (index) => {
|
const onDrop = async (index) => {
|
||||||
if (dragIndex.value === null || dragIndex.value === index) {
|
if (dragIndex.value === null || dragIndex.value === index) {
|
||||||
dragIndex.value = null
|
dragIndex.value = null
|
||||||
dragOverIndex.value = null
|
dragOverIndex.value = null
|
||||||
@@ -37,6 +37,8 @@ export function useDraggable(list) {
|
|||||||
|
|
||||||
dragIndex.value = null
|
dragIndex.value = null
|
||||||
dragOverIndex.value = null
|
dragOverIndex.value = null
|
||||||
|
|
||||||
|
if (onReorder) await onReorder(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDragEnd = (event) => {
|
const onDragEnd = (event) => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export const useCertsStore = defineStore('certs', {
|
export const useCertsStore = defineStore('certs', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -8,37 +8,62 @@ export const useCertsStore = defineStore('certs', {
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async loadAll() {
|
async loadAll() {
|
||||||
const data = await api.getAllCertsInfo()
|
try {
|
||||||
if (data) {
|
const data = await api.getAllCertsInfo()
|
||||||
this.list = data
|
if (data) {
|
||||||
this.loaded = true
|
this.list = data
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load certs:', e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async getInfo(domain) {
|
async getInfo(domain) {
|
||||||
return await api.getCertInfo(domain)
|
try {
|
||||||
|
return await api.getCertInfo(domain)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to get cert info:', e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async _obtainAndReload(domain) {
|
||||||
|
try {
|
||||||
|
const result = await api.obtainSSLCertificate(domain)
|
||||||
|
await this.loadAll()
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to obtain certificate:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async issue(domain) {
|
async issue(domain) {
|
||||||
const result = await api.obtainSSLCertificate(domain)
|
return this._obtainAndReload(domain)
|
||||||
await this.loadAll()
|
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async renew(domain) {
|
async renew(domain) {
|
||||||
const result = await api.obtainSSLCertificate(domain)
|
return this._obtainAndReload(domain)
|
||||||
await this.loadAll()
|
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async remove(domain) {
|
async remove(domain) {
|
||||||
const result = await api.deleteCertificate(domain)
|
try {
|
||||||
await this.loadAll()
|
const result = await api.deleteCertificate(domain)
|
||||||
return result
|
await this.loadAll()
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to delete certificate:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async reload() {
|
async reload() {
|
||||||
return await api.reloadSSLCertificates()
|
try {
|
||||||
|
return await api.reloadSSLCertificates()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to reload certificates:', e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export const useConfigStore = defineStore('config', {
|
export const useConfigStore = defineStore('config', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -12,39 +12,28 @@ export const useConfigStore = defineStore('config', {
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async load() {
|
async load() {
|
||||||
const config = await api.getConfig()
|
try {
|
||||||
if (config) {
|
const config = await api.getConfig()
|
||||||
this.data = config
|
if (config) {
|
||||||
this.loaded = true
|
this.data = config
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load config:', e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async save(configData) {
|
async save(configData) {
|
||||||
const result = await api.saveConfig(JSON.stringify(configData, null, 4))
|
try {
|
||||||
if (result && !String(result).startsWith('Error')) {
|
const result = await api.saveConfig(JSON.stringify(configData, null, 4))
|
||||||
this.data = configData
|
if (isSuccess(result)) {
|
||||||
|
this.data = configData
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save config:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
},
|
|
||||||
|
|
||||||
async enableProxy() {
|
|
||||||
return await api.enableProxyService()
|
|
||||||
},
|
|
||||||
|
|
||||||
async disableProxy() {
|
|
||||||
return await api.disableProxyService()
|
|
||||||
},
|
|
||||||
|
|
||||||
async enableACME() {
|
|
||||||
return await api.enableACMEService()
|
|
||||||
},
|
|
||||||
|
|
||||||
async disableACME() {
|
|
||||||
return await api.disableACMEService()
|
|
||||||
},
|
|
||||||
|
|
||||||
async restartAll() {
|
|
||||||
return await api.restartAllServices()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export const useProxiesStore = defineStore('proxies', {
|
export const useProxiesStore = defineStore('proxies', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -8,44 +8,58 @@ export const useProxiesStore = defineStore('proxies', {
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async load() {
|
async load() {
|
||||||
const data = await api.getProxyList()
|
try {
|
||||||
if (data) {
|
const data = await api.getProxyList()
|
||||||
this.list = data
|
if (data) {
|
||||||
this.loaded = true
|
this.list = data
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load proxies:', e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(proxyData) {
|
async create(proxyData) {
|
||||||
const config = await api.getConfig()
|
try {
|
||||||
config.Proxy_Service.push({
|
const config = await api.getConfig()
|
||||||
Name: proxyData.name || '',
|
config.Proxy_Service.push({
|
||||||
Enable: proxyData.enabled,
|
Name: proxyData.name || '',
|
||||||
ExternalDomain: proxyData.domain,
|
Enable: proxyData.enabled,
|
||||||
LocalAddress: proxyData.localAddr,
|
ExternalDomain: proxyData.domain,
|
||||||
LocalPort: proxyData.localPort,
|
LocalAddress: proxyData.localAddr,
|
||||||
ServiceHTTPSuse: proxyData.serviceHttps,
|
LocalPort: proxyData.localPort,
|
||||||
AutoHTTPS: proxyData.autoHttps,
|
ServiceHTTPSuse: proxyData.serviceHttps,
|
||||||
AutoCreateSSL: proxyData.autoSSL,
|
AutoHTTPS: proxyData.autoHttps,
|
||||||
})
|
AutoCreateSSL: proxyData.autoSSL,
|
||||||
const result = await api.saveConfig(JSON.stringify(config))
|
})
|
||||||
if (result && !String(result).startsWith('Error')) {
|
const result = await api.saveConfig(JSON.stringify(config))
|
||||||
await api.reloadConfig()
|
if (isSuccess(result)) {
|
||||||
await this.load()
|
await api.reloadConfig()
|
||||||
|
await this.load()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to create proxy:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async remove(domain) {
|
async remove(domain) {
|
||||||
const config = await api.getConfig()
|
try {
|
||||||
config.Proxy_Service = config.Proxy_Service.filter(
|
const config = await api.getConfig()
|
||||||
p => p.ExternalDomain !== domain
|
config.Proxy_Service = config.Proxy_Service.filter(
|
||||||
)
|
p => p.ExternalDomain !== domain
|
||||||
const result = await api.saveConfig(JSON.stringify(config))
|
)
|
||||||
if (result && !String(result).startsWith('Error')) {
|
const result = await api.saveConfig(JSON.stringify(config))
|
||||||
await api.reloadConfig()
|
if (isSuccess(result)) {
|
||||||
await this.load()
|
await api.reloadConfig()
|
||||||
|
await this.load()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to remove proxy:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
const SERVICE_METHODS = {
|
||||||
|
HTTP: { start: () => api.startHTTPService(), stop: () => api.stopHTTPService() },
|
||||||
|
HTTPS: { start: () => api.startHTTPSService(), stop: () => api.stopHTTPSService() },
|
||||||
|
MySQL: { start: () => api.startMySQLService(), stop: () => api.stopMySQLService() },
|
||||||
|
PHP: { start: () => api.startPHPService(), stop: () => api.stopPHPService() },
|
||||||
|
}
|
||||||
|
|
||||||
export const useServicesStore = defineStore('services', {
|
export const useServicesStore = defineStore('services', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -9,10 +16,14 @@ export const useServicesStore = defineStore('services', {
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async load() {
|
async load() {
|
||||||
const data = await api.getAllServicesStatus()
|
try {
|
||||||
if (data) {
|
const data = await api.getAllServicesStatus()
|
||||||
this.list = Array.isArray(data) ? data : Object.values(data)
|
if (data) {
|
||||||
this.loaded = true
|
this.list = Array.isArray(data) ? data : Object.values(data)
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load services:', e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -26,26 +37,62 @@ export const useServicesStore = defineStore('services', {
|
|||||||
this.isOperating = false
|
this.isOperating = false
|
||||||
},
|
},
|
||||||
|
|
||||||
async startService(name) {
|
async toggleService(name, action) {
|
||||||
const methods = {
|
try {
|
||||||
HTTP: () => api.startHTTPService(),
|
const method = SERVICE_METHODS[name]?.[action]
|
||||||
HTTPS: () => api.startHTTPSService(),
|
if (method) await method()
|
||||||
MySQL: () => api.startMySQLService(),
|
await this.load()
|
||||||
PHP: () => api.startPHPService(),
|
} catch (e) {
|
||||||
|
console.error(`Failed to ${action} service ${name}:`, e)
|
||||||
}
|
}
|
||||||
if (methods[name]) await methods[name]()
|
},
|
||||||
await this.load()
|
|
||||||
|
async startService(name) {
|
||||||
|
return this.toggleService(name, 'start')
|
||||||
},
|
},
|
||||||
|
|
||||||
async stopService(name) {
|
async stopService(name) {
|
||||||
const methods = {
|
return this.toggleService(name, 'stop')
|
||||||
HTTP: () => api.stopHTTPService(),
|
},
|
||||||
HTTPS: () => api.stopHTTPSService(),
|
|
||||||
MySQL: () => api.stopMySQLService(),
|
async enableProxy() {
|
||||||
PHP: () => api.stopPHPService(),
|
try {
|
||||||
|
return await api.enableProxyService()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to enable proxy:', e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async disableProxy() {
|
||||||
|
try {
|
||||||
|
return await api.disableProxyService()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to disable proxy:', e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async enableACME() {
|
||||||
|
try {
|
||||||
|
return await api.enableACMEService()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to enable ACME:', e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async disableACME() {
|
||||||
|
try {
|
||||||
|
return await api.disableACMEService()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to disable ACME:', e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async restartAll() {
|
||||||
|
try {
|
||||||
|
return await api.restartAllServices()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to restart services:', e)
|
||||||
}
|
}
|
||||||
if (methods[name]) await methods[name]()
|
|
||||||
await this.load()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export const useSitesStore = defineStore('sites', {
|
export const useSitesStore = defineStore('sites', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
@@ -8,35 +8,58 @@ export const useSitesStore = defineStore('sites', {
|
|||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
async load() {
|
async load() {
|
||||||
const data = await api.getSitesList()
|
try {
|
||||||
if (data) {
|
const data = await api.getSitesList()
|
||||||
this.list = data
|
if (data) {
|
||||||
this.loaded = true
|
this.list = data
|
||||||
|
this.loaded = true
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load sites:', e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(siteData) {
|
async create(siteData) {
|
||||||
const result = await api.createNewSite(JSON.stringify(siteData))
|
try {
|
||||||
if (result && !String(result).startsWith('Error')) {
|
const result = await api.createNewSite(JSON.stringify(siteData))
|
||||||
await this.load()
|
if (isSuccess(result)) {
|
||||||
|
await this.load()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to create site:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async remove(host) {
|
async remove(host) {
|
||||||
const result = await api.deleteSite(host)
|
try {
|
||||||
if (result && !String(result).startsWith('Error')) {
|
const result = await api.deleteSite(host)
|
||||||
await this.load()
|
if (isSuccess(result)) {
|
||||||
|
await this.load()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to delete site:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async openFolder(host) {
|
async openFolder(host) {
|
||||||
await api.openSiteFolder(host)
|
try {
|
||||||
|
await api.openSiteFolder(host)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to open folder:', e)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async uploadCert(host, certType, certDataBase64) {
|
async uploadCert(host, certType, certDataBase64) {
|
||||||
return await api.uploadCertificate(host, certType, certDataBase64)
|
try {
|
||||||
|
return await api.uploadCertificate(host, certType, certDataBase64)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to upload cert:', e)
|
||||||
|
return `Error: ${e.message}`
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
1
front_vue/src/Core/utils/apiResult.js
Normal file
1
front_vue/src/Core/utils/apiResult.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const isSuccess = (result) => result && !String(result).startsWith('Error')
|
||||||
7
front_vue/src/Core/utils/openUrl.js
Normal file
7
front_vue/src/Core/utils/openUrl.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function openUrl(url) {
|
||||||
|
if (window.runtime?.BrowserOpenURL) {
|
||||||
|
window.runtime.BrowserOpenURL(url)
|
||||||
|
} else {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
}
|
||||||
1
front_vue/src/Core/utils/sleep.js
Normal file
1
front_vue/src/Core/utils/sleep.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
.form-subsection-title i {
|
.form-subsection-title i {
|
||||||
color: var(--accent-purple-light);
|
color: var(--accent-purple-light);
|
||||||
font-size: 16px;
|
font-size: var(--text-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
.data-table th {
|
.data-table th {
|
||||||
padding: 8px 20px;
|
padding: 8px 20px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 11px;
|
font-size: var(--text-sm);
|
||||||
font-weight: var(--font-semibold);
|
font-weight: var(--font-semibold);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
.th-icon {
|
.th-icon {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
font-size: 10px;
|
font-size: var(--text-xs);
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,12 +152,12 @@
|
|||||||
cursor: wait;
|
cursor: wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-toggle {
|
.data-table .status-toggle {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: opacity var(--transition-fast);
|
transition: opacity var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-toggle:hover {
|
.data-table .status-toggle:hover {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
opacity: 0.15;
|
opacity: 0.15;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
font-size: 11px;
|
font-size: var(--text-sm);
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
transition: all var(--transition-fast);
|
transition: all var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,12 @@
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fade In (для страниц и компонентов) */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(8px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
/* Notification slide */
|
/* Notification slide */
|
||||||
.notification-enter-active {
|
.notification-enter-active {
|
||||||
transition: all 0.3s ease-out;
|
transition: all 0.3s ease-out;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`ndefineProps({ cert: Object })`n</script>`n<template><div class="cert-card">{{ cert?.domain }}</div></template>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("certs.title") }}</div></template>
|
|
||||||
@@ -2,19 +2,11 @@
|
|||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const currentYear = new Date().getFullYear()
|
const currentYear = new Date().getFullYear()
|
||||||
|
|
||||||
const openSite = () => {
|
|
||||||
if (window.runtime?.BrowserOpenURL) {
|
|
||||||
window.runtime.BrowserOpenURL('https://vserf.ru')
|
|
||||||
} else {
|
|
||||||
window.open('https://vserf.ru', '_blank')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<p>vServer Admin Panel © 2025-{{ currentYear }} | <a href="#" class="footer-link" @click.prevent="openSite">https://vserf.ru</a> | {{ t('app.footerAuthor') }}</p>
|
<p>vServer Admin Panel © 2025-{{ currentYear }} | <a href="#" class="footer-link" @click.prevent="openUrl('https://vserf.ru')">https://vserf.ru</a> | {{ t('app.footerAuthor') }}</p>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
@@ -10,8 +10,6 @@ const { confirm } = useConfirm()
|
|||||||
const operating = ref(false)
|
const operating = ref(false)
|
||||||
const statusLabel = ref('')
|
const statusLabel = ref('')
|
||||||
|
|
||||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
|
||||||
|
|
||||||
const toggleLocale = () => {
|
const toggleLocale = () => {
|
||||||
const next = locale.value === 'ru' ? 'en' : 'ru'
|
const next = locale.value === 'ru' ? 'en' : 'ru'
|
||||||
locale.value = next
|
locale.value = next
|
||||||
|
|||||||
@@ -1,31 +1,24 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const proxiesStore = useProxiesStore()
|
const proxiesStore = useProxiesStore()
|
||||||
const certsStore = useCertsStore()
|
const { findCertForDomain } = useCertLookup()
|
||||||
|
|
||||||
const togglingStatus = ref('')
|
const togglingStatus = ref('')
|
||||||
const dragIdx = ref(null)
|
|
||||||
const overIdx = ref(null)
|
|
||||||
|
|
||||||
const onDragStart = (i, e) => { dragIdx.value = i; e.dataTransfer.effectAllowed = 'move' }
|
const proxyList = computed({
|
||||||
const onDragOver = (i, e) => { e.preventDefault(); overIdx.value = i }
|
get: () => proxiesStore.list,
|
||||||
const onDragLeave = () => { overIdx.value = null }
|
set: (v) => { proxiesStore.list = v },
|
||||||
const onDragEnd = () => { dragIdx.value = null; overIdx.value = null }
|
})
|
||||||
|
|
||||||
const onDrop = async (i) => {
|
const { dragIndex, dragOverIndex, onDragStart, onDragOver, onDragLeave, onDrop, onDragEnd } = useDraggable(proxyList, {
|
||||||
if (dragIdx.value === null || dragIdx.value === i) { dragIdx.value = null; overIdx.value = null; return }
|
onReorder: async (items) => {
|
||||||
const items = [...proxiesStore.list]
|
const config = await api.getConfig()
|
||||||
const [moved] = items.splice(dragIdx.value, 1)
|
config.Proxy_Service = items.map(p => config.Proxy_Service.find(c => c.ExternalDomain === p.ExternalDomain)).filter(Boolean)
|
||||||
items.splice(i, 0, moved)
|
await api.saveConfig(JSON.stringify(config))
|
||||||
proxiesStore.list = items
|
},
|
||||||
dragIdx.value = null
|
})
|
||||||
overIdx.value = null
|
|
||||||
const config = await api.getConfig()
|
|
||||||
config.Proxy_Service = items.map(p => config.Proxy_Service.find(c => c.ExternalDomain === p.ExternalDomain)).filter(Boolean)
|
|
||||||
await api.saveConfig(JSON.stringify(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleStatus = async (proxy) => {
|
const toggleStatus = async (proxy) => {
|
||||||
togglingStatus.value = proxy.ExternalDomain
|
togglingStatus.value = proxy.ExternalDomain
|
||||||
@@ -39,40 +32,6 @@ const toggleStatus = async (proxy) => {
|
|||||||
}
|
}
|
||||||
togglingStatus.value = ''
|
togglingStatus.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const openUrl = (host) => {
|
|
||||||
if (window.runtime?.BrowserOpenURL) {
|
|
||||||
window.runtime.BrowserOpenURL('http://' + host)
|
|
||||||
} else {
|
|
||||||
window.open('http://' + host, '_blank')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const findCertForDomain = (domain) => {
|
|
||||||
const direct = certsStore.list.find(c => c.domain === domain && c.has_cert)
|
|
||||||
if (direct) return direct
|
|
||||||
|
|
||||||
const parts = domain.split('.')
|
|
||||||
if (parts.length >= 2) {
|
|
||||||
const wildcard = '*.' + parts.slice(1).join('.')
|
|
||||||
const wc = certsStore.list.find(c => c.domain === wildcard && c.has_cert)
|
|
||||||
if (wc) return wc
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const cert of certsStore.list) {
|
|
||||||
if (cert.has_cert && cert.dns_names) {
|
|
||||||
for (const dns of cert.dns_names) {
|
|
||||||
if (dns === domain) return cert
|
|
||||||
if (dns.startsWith('*.')) {
|
|
||||||
const base = dns.slice(2)
|
|
||||||
const dParts = domain.split('.')
|
|
||||||
if (dParts.length >= 2 && dParts.slice(1).join('.') === base) return cert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -95,12 +54,12 @@ const findCertForDomain = (domain) => {
|
|||||||
v-for="(proxy, i) in proxiesStore.list"
|
v-for="(proxy, i) in proxiesStore.list"
|
||||||
:key="proxy.ExternalDomain"
|
:key="proxy.ExternalDomain"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
:class="{ 'drag-over': overIdx === i, 'dragging': dragIdx === i }"
|
:class="{ 'drag-over': dragOverIndex === i, 'dragging': dragIndex === i }"
|
||||||
@dragstart="onDragStart(i, $event)"
|
@dragstart="onDragStart(i, $event)"
|
||||||
@dragover="onDragOver(i, $event)"
|
@dragover="onDragOver(i, $event)"
|
||||||
@dragleave="onDragLeave"
|
@dragleave="onDragLeave"
|
||||||
@drop="onDrop(i)"
|
@drop="onDrop(i)"
|
||||||
@dragend="onDragEnd"
|
@dragend="onDragEnd($event)"
|
||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<i class="fas fa-grip-vertical drag-grip"></i>
|
<i class="fas fa-grip-vertical drag-grip"></i>
|
||||||
@@ -110,7 +69,7 @@ const findCertForDomain = (domain) => {
|
|||||||
{{ proxy.Name || '—' }}
|
{{ proxy.Name || '—' }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code class="clickable-link" @click="openUrl(proxy.ExternalDomain)">{{ proxy.ExternalDomain }} <i class="fas fa-external-link-alt"></i></code>
|
<code class="clickable-link" @click="openUrl('http://' + proxy.ExternalDomain)">{{ proxy.ExternalDomain }} <i class="fas fa-external-link-alt"></i></code>
|
||||||
</td>
|
</td>
|
||||||
<td><code>{{ proxy.LocalAddress }}:{{ proxy.LocalPort }}</code></td>
|
<td><code>{{ proxy.LocalAddress }}:{{ proxy.LocalPort }}</code></td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("common.edit") }}</div></template>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("common.edit") }}</div></template>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
test
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("common.edit") }}</div></template>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("common.edit") }}</div></template>
|
|
||||||
@@ -1,36 +1,27 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const sitesStore = useSitesStore()
|
const sitesStore = useSitesStore()
|
||||||
const certsStore = useCertsStore()
|
|
||||||
const { success, error } = useNotification()
|
const { success, error } = useNotification()
|
||||||
const { confirmDelete: showConfirm } = useConfirm()
|
const { confirmDelete: showConfirm } = useConfirm()
|
||||||
|
const { findCertForDomain } = useCertLookup()
|
||||||
|
|
||||||
const dragIdx = ref(null)
|
|
||||||
const overIdx = ref(null)
|
|
||||||
|
|
||||||
const onDragStart = (i, e) => { dragIdx.value = i; e.dataTransfer.effectAllowed = 'move' }
|
|
||||||
const onDragOver = (i, e) => { e.preventDefault(); overIdx.value = i }
|
|
||||||
const onDragLeave = () => { overIdx.value = null }
|
|
||||||
const onDragEnd = () => { dragIdx.value = null; overIdx.value = null }
|
|
||||||
|
|
||||||
const onDrop = async (i) => {
|
|
||||||
if (dragIdx.value === null || dragIdx.value === i) { dragIdx.value = null; overIdx.value = null; return }
|
|
||||||
const items = [...sitesStore.list]
|
|
||||||
const [moved] = items.splice(dragIdx.value, 1)
|
|
||||||
items.splice(i, 0, moved)
|
|
||||||
sitesStore.list = items
|
|
||||||
dragIdx.value = null
|
|
||||||
overIdx.value = null
|
|
||||||
const config = await api.getConfig()
|
|
||||||
config.Site_www = items.map(s => config.Site_www.find(c => c.host === s.host)).filter(Boolean)
|
|
||||||
await api.saveConfig(JSON.stringify(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
const openingFolder = ref('')
|
const openingFolder = ref('')
|
||||||
const togglingStatus = ref('')
|
const togglingStatus = ref('')
|
||||||
|
|
||||||
|
const siteList = computed({
|
||||||
|
get: () => sitesStore.list,
|
||||||
|
set: (v) => { sitesStore.list = v },
|
||||||
|
})
|
||||||
|
|
||||||
|
const { dragIndex, dragOverIndex, onDragStart, onDragOver, onDragLeave, onDrop, onDragEnd } = useDraggable(siteList, {
|
||||||
|
onReorder: async (items) => {
|
||||||
|
const config = await api.getConfig()
|
||||||
|
config.Site_www = items.map(s => config.Site_www.find(c => c.host === s.host)).filter(Boolean)
|
||||||
|
await api.saveConfig(JSON.stringify(config))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const toggleStatus = async (site) => {
|
const toggleStatus = async (site) => {
|
||||||
togglingStatus.value = site.host
|
togglingStatus.value = site.host
|
||||||
const newStatus = site.status === 'active' ? 'inactive' : 'active'
|
const newStatus = site.status === 'active' ? 'inactive' : 'active'
|
||||||
@@ -45,49 +36,12 @@ const toggleStatus = async (site) => {
|
|||||||
togglingStatus.value = ''
|
togglingStatus.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const openUrl = (host) => {
|
|
||||||
if (window.runtime?.BrowserOpenURL) {
|
|
||||||
window.runtime.BrowserOpenURL('http://' + host)
|
|
||||||
} else {
|
|
||||||
window.open('http://' + host, '_blank')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const openFolder = async (host) => {
|
const openFolder = async (host) => {
|
||||||
openingFolder.value = host
|
openingFolder.value = host
|
||||||
await sitesStore.openFolder(host)
|
await sitesStore.openFolder(host)
|
||||||
setTimeout(() => { openingFolder.value = '' }, 800)
|
setTimeout(() => { openingFolder.value = '' }, 800)
|
||||||
}
|
}
|
||||||
|
|
||||||
const findCertForDomain = (domain, aliases = []) => {
|
|
||||||
const allDomains = [domain, ...aliases.filter(a => !a.includes('*'))]
|
|
||||||
for (const d of allDomains) {
|
|
||||||
const direct = certsStore.list.find(c => c.domain === d && c.has_cert)
|
|
||||||
if (direct) return direct
|
|
||||||
|
|
||||||
const parts = d.split('.')
|
|
||||||
if (parts.length >= 2) {
|
|
||||||
const wildcard = '*.' + parts.slice(1).join('.')
|
|
||||||
const wc = certsStore.list.find(c => c.domain === wildcard && c.has_cert)
|
|
||||||
if (wc) return wc
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const cert of certsStore.list) {
|
|
||||||
if (cert.has_cert && cert.dns_names) {
|
|
||||||
for (const dns of cert.dns_names) {
|
|
||||||
if (dns === d) return cert
|
|
||||||
if (dns.startsWith('*.')) {
|
|
||||||
const base = dns.slice(2)
|
|
||||||
const dParts = d.split('.')
|
|
||||||
if (dParts.length >= 2 && dParts.slice(1).join('.') === base) return cert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmDelete = async (site) => {
|
const confirmDelete = async (site) => {
|
||||||
const result = await showConfirm({
|
const result = await showConfirm({
|
||||||
title: t('sites.deleteTitle'),
|
title: t('sites.deleteTitle'),
|
||||||
@@ -95,7 +49,7 @@ const confirmDelete = async (site) => {
|
|||||||
})
|
})
|
||||||
if (result) {
|
if (result) {
|
||||||
const res = await sitesStore.remove(site.host)
|
const res = await sitesStore.remove(site.host)
|
||||||
if (res && !String(res).startsWith('Error')) success(t('notify.siteDeleted'))
|
if (isSuccess(res)) success(t('notify.siteDeleted'))
|
||||||
else error(String(res))
|
else error(String(res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,12 +75,12 @@ const confirmDelete = async (site) => {
|
|||||||
v-for="(site, i) in sitesStore.list"
|
v-for="(site, i) in sitesStore.list"
|
||||||
:key="site.host"
|
:key="site.host"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
:class="{ 'drag-over': overIdx === i, 'dragging': dragIdx === i }"
|
:class="{ 'drag-over': dragOverIndex === i, 'dragging': dragIndex === i }"
|
||||||
@dragstart="onDragStart(i, $event)"
|
@dragstart="onDragStart(i, $event)"
|
||||||
@dragover="onDragOver(i, $event)"
|
@dragover="onDragOver(i, $event)"
|
||||||
@dragleave="onDragLeave"
|
@dragleave="onDragLeave"
|
||||||
@drop="onDrop(i)"
|
@drop="onDrop(i)"
|
||||||
@dragend="onDragEnd"
|
@dragend="onDragEnd($event)"
|
||||||
>
|
>
|
||||||
<td>
|
<td>
|
||||||
<i class="fas fa-grip-vertical drag-grip"></i>
|
<i class="fas fa-grip-vertical drag-grip"></i>
|
||||||
@@ -136,7 +90,7 @@ const confirmDelete = async (site) => {
|
|||||||
{{ site.name }}
|
{{ site.name }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<code class="clickable-link" @click="openUrl(site.host)">{{ site.host }} <i class="fas fa-external-link-alt"></i></code>
|
<code class="clickable-link" @click="openUrl('http://' + site.host)">{{ site.host }} <i class="fas fa-external-link-alt"></i></code>
|
||||||
</td>
|
</td>
|
||||||
<td><code>{{ site.alias?.join(', ') || '—' }}</code></td>
|
<td><code>{{ site.alias?.join(', ') || '—' }}</code></td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("vaccess.helpTab") }}</div></template>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>Rule Row</div></template>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<script setup>`nconst { t } = useI18n()`n</script>`n<template><div>{{ t("vaccess.title") }}</div></template>
|
|
||||||
@@ -13,8 +13,6 @@ const issuing = ref('')
|
|||||||
const renewing = ref('')
|
const renewing = ref('')
|
||||||
const deleting = ref('')
|
const deleting = ref('')
|
||||||
|
|
||||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
|
||||||
|
|
||||||
const refreshCerts = async () => {
|
const refreshCerts = async () => {
|
||||||
await certsStore.loadAll()
|
await certsStore.loadAll()
|
||||||
certs.value = certsStore.list.filter(c =>
|
certs.value = certsStore.list.filter(c =>
|
||||||
@@ -34,7 +32,7 @@ onMounted(async () => {
|
|||||||
const issueCert = async (domain) => {
|
const issueCert = async (domain) => {
|
||||||
issuing.value = domain
|
issuing.value = domain
|
||||||
const [result] = await Promise.all([certsStore.issue(domain), sleep(1000)])
|
const [result] = await Promise.all([certsStore.issue(domain), sleep(1000)])
|
||||||
if (result && !String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
success(t('notify.certIssued'))
|
success(t('notify.certIssued'))
|
||||||
await refreshCerts()
|
await refreshCerts()
|
||||||
} else {
|
} else {
|
||||||
@@ -46,7 +44,7 @@ const issueCert = async (domain) => {
|
|||||||
const renewCert = async (domain) => {
|
const renewCert = async (domain) => {
|
||||||
renewing.value = domain
|
renewing.value = domain
|
||||||
const [result] = await Promise.all([certsStore.renew(domain), sleep(1000)])
|
const [result] = await Promise.all([certsStore.renew(domain), sleep(1000)])
|
||||||
if (result && !String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
success(t('notify.certRenewed'))
|
success(t('notify.certRenewed'))
|
||||||
await refreshCerts()
|
await refreshCerts()
|
||||||
} else {
|
} else {
|
||||||
@@ -58,7 +56,7 @@ const renewCert = async (domain) => {
|
|||||||
const deleteCert = async (domain) => {
|
const deleteCert = async (domain) => {
|
||||||
deleting.value = domain
|
deleting.value = domain
|
||||||
const [result] = await Promise.all([certsStore.remove(domain), sleep(1000)])
|
const [result] = await Promise.all([certsStore.remove(domain), sleep(1000)])
|
||||||
if (result && !String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
success(t('notify.certDeleted'))
|
success(t('notify.certDeleted'))
|
||||||
await refreshCerts()
|
await refreshCerts()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const createProxy = async () => {
|
|||||||
autoSSL: false,
|
autoSSL: false,
|
||||||
})
|
})
|
||||||
creating.value = false
|
creating.value = false
|
||||||
if (result && !String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
success(t('notify.proxyCreated'))
|
success(t('notify.proxyCreated'))
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const proxiesStore = useProxiesStore()
|
const proxiesStore = useProxiesStore()
|
||||||
@@ -52,7 +52,7 @@ const saveProxy = async () => {
|
|||||||
config.Proxy_Service[idx].AutoHTTPS = form.serviceHttps
|
config.Proxy_Service[idx].AutoHTTPS = form.serviceHttps
|
||||||
config.Proxy_Service[idx].AutoCreateSSL = form.autoSSL
|
config.Proxy_Service[idx].AutoCreateSSL = form.autoSSL
|
||||||
const result = await api.saveConfig(JSON.stringify(config))
|
const result = await api.saveConfig(JSON.stringify(config))
|
||||||
if (!String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
await proxiesStore.load()
|
await proxiesStore.load()
|
||||||
success(t('notify.dataSaved'))
|
success(t('notify.dataSaved'))
|
||||||
router.push('/')
|
router.push('/')
|
||||||
@@ -72,7 +72,7 @@ const confirmDelete = async () => {
|
|||||||
})
|
})
|
||||||
if (result) {
|
if (result) {
|
||||||
const res = await proxiesStore.remove(form.domain)
|
const res = await proxiesStore.remove(form.domain)
|
||||||
if (res && !String(res).startsWith('Error')) {
|
if (isSuccess(res)) {
|
||||||
success(t('notify.proxyDeleted'))
|
success(t('notify.proxyDeleted'))
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
const servicesStore = useServicesStore()
|
||||||
const { success } = useNotification()
|
const { success } = useNotification()
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -39,22 +40,22 @@ const saveSettings = async () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
await configStore.save(configData)
|
await configStore.save(configData)
|
||||||
await configStore.restartAll()
|
await servicesStore.restartAll()
|
||||||
saving.value = false
|
saving.value = false
|
||||||
success(t('notify.settingsSaved'))
|
success(t('notify.settingsSaved'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleProxy = async () => {
|
const toggleProxy = async () => {
|
||||||
form.proxyEnabled = !form.proxyEnabled
|
form.proxyEnabled = !form.proxyEnabled
|
||||||
if (form.proxyEnabled) await configStore.enableProxy()
|
if (form.proxyEnabled) await servicesStore.enableProxy()
|
||||||
else await configStore.disableProxy()
|
else await servicesStore.disableProxy()
|
||||||
success(form.proxyEnabled ? t('notify.proxyEnabled') : t('notify.proxyDisabled'))
|
success(form.proxyEnabled ? t('notify.proxyEnabled') : t('notify.proxyDisabled'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleAcme = async () => {
|
const toggleAcme = async () => {
|
||||||
form.acmeEnabled = !form.acmeEnabled
|
form.acmeEnabled = !form.acmeEnabled
|
||||||
if (form.acmeEnabled) await configStore.enableACME()
|
if (form.acmeEnabled) await servicesStore.enableACME()
|
||||||
else await configStore.disableACME()
|
else await servicesStore.disableACME()
|
||||||
success(form.acmeEnabled ? t('notify.certManagerEnabled') : t('notify.certManagerDisabled'))
|
success(form.acmeEnabled ? t('notify.certManagerEnabled') : t('notify.certManagerDisabled'))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ const createSite = async () => {
|
|||||||
}
|
}
|
||||||
const result = await sitesStore.create(siteData)
|
const result = await sitesStore.create(siteData)
|
||||||
creating.value = false
|
creating.value = false
|
||||||
if (result && !String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
success(t('notify.siteCreated'))
|
success(t('notify.siteCreated'))
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const sitesStore = useSitesStore()
|
const sitesStore = useSitesStore()
|
||||||
@@ -67,7 +67,7 @@ const saveSite = async () => {
|
|||||||
config.Site_www[idx].root_file_routing = form.routing
|
config.Site_www[idx].root_file_routing = form.routing
|
||||||
config.Site_www[idx].AutoCreateSSL = form.autoSSL
|
config.Site_www[idx].AutoCreateSSL = form.autoSSL
|
||||||
const result = await api.saveConfig(JSON.stringify(config))
|
const result = await api.saveConfig(JSON.stringify(config))
|
||||||
if (!String(result).startsWith('Error')) {
|
if (isSuccess(result)) {
|
||||||
await sitesStore.load()
|
await sitesStore.load()
|
||||||
success(t('notify.dataSaved'))
|
success(t('notify.dataSaved'))
|
||||||
router.push('/')
|
router.push('/')
|
||||||
@@ -87,7 +87,7 @@ const confirmDelete = async () => {
|
|||||||
})
|
})
|
||||||
if (result) {
|
if (result) {
|
||||||
const res = await sitesStore.remove(form.host)
|
const res = await sitesStore.remove(form.host)
|
||||||
if (res && !String(res).startsWith('Error')) {
|
if (isSuccess(res)) {
|
||||||
success(t('notify.siteDeleted'))
|
success(t('notify.siteDeleted'))
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export default defineConfig({
|
|||||||
dirs: [
|
dirs: [
|
||||||
'src/Core/composables',
|
'src/Core/composables',
|
||||||
'src/Core/stores',
|
'src/Core/stores',
|
||||||
|
'src/Core/utils',
|
||||||
],
|
],
|
||||||
vueTemplate: true,
|
vueTemplate: true,
|
||||||
dts: 'src/auto-imports.d.ts',
|
dts: 'src/auto-imports.d.ts',
|
||||||
@@ -40,9 +41,7 @@ export default defineConfig({
|
|||||||
'src/Design/components/services',
|
'src/Design/components/services',
|
||||||
'src/Design/components/sites',
|
'src/Design/components/sites',
|
||||||
'src/Design/components/proxies',
|
'src/Design/components/proxies',
|
||||||
'src/Design/components/vaccess',
|
'src/Design/components/vaccess', ],
|
||||||
'src/Design/components/certs',
|
|
||||||
],
|
|
||||||
dts: 'src/components.d.ts',
|
dts: 'src/components.d.ts',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user