update
parent
ab165c0002
commit
15e4a8062a
|
@ -14,11 +14,6 @@ __dir__ = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
web_root = os.path.join(__dir__, './webapp')
|
web_root = os.path.join(__dir__, './webapp')
|
||||||
home_dir = os.getenv('HOME')
|
home_dir = os.getenv('HOME')
|
||||||
config_dir = os.path.join(home_dir, '.config/dooke')
|
|
||||||
|
|
||||||
|
|
||||||
if not os.path.isdir(config_dir):
|
|
||||||
os.mkdir(config_dir)
|
|
||||||
|
|
||||||
|
|
||||||
class Application(Gtk.Application):
|
class Application(Gtk.Application):
|
||||||
|
|
|
@ -6,10 +6,21 @@
|
||||||
|
|
||||||
import 'es.shim'
|
import 'es.shim'
|
||||||
import { html, css, Component } from 'wkit'
|
import { html, css, Component } from 'wkit'
|
||||||
import { createApp, createRouter } from 'wkitd'
|
import { createApp } from 'wkitd'
|
||||||
|
|
||||||
|
import 'ui/icon/index.js'
|
||||||
|
import 'ui/scroll/index.js'
|
||||||
|
import 'ui/layer/index.js'
|
||||||
|
import 'ui/space/index.js'
|
||||||
|
import 'ui/form/input.js'
|
||||||
|
import 'ui/form/switch.js'
|
||||||
|
import 'ui/form/button.js'
|
||||||
|
import 'ui/form/link.js'
|
||||||
|
|
||||||
|
import router from './router.js'
|
||||||
import store from './store.js'
|
import store from './store.js'
|
||||||
import './components/home.js'
|
|
||||||
|
import '../components/sidebar.js'
|
||||||
|
|
||||||
import { noop } from './utils/index.js'
|
import { noop } from './utils/index.js'
|
||||||
|
|
||||||
|
@ -26,6 +37,11 @@ createApp({
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router-view {
|
||||||
|
flex: 1;
|
||||||
|
height: 100;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
],
|
],
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -34,8 +50,18 @@ createApp({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
return html` <wc-home @contextmenu.prevent=${noop}></wc-home> `
|
return html`
|
||||||
|
<wc-sidebar></wc-sidebar>
|
||||||
|
<router-view></router-view>
|
||||||
|
<wc-layer ref="context" left="100px" top="0" radius="0">
|
||||||
|
<ul class="context-menu" @click=${this.confirmAction}>
|
||||||
|
<li class="item" data-act="del">删除域名</li>
|
||||||
|
<li class="item" data-act="edit">修改域名</li>
|
||||||
|
</ul>
|
||||||
|
</wc-layer>
|
||||||
|
`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.use(router)
|
||||||
.use(store)
|
.use(store)
|
||||||
.mount()
|
.mount()
|
||||||
|
|
|
@ -1,262 +0,0 @@
|
||||||
/**
|
|
||||||
* {}
|
|
||||||
* @author yutent<yutent.io@gmail.com>
|
|
||||||
* @date 2023/08/08 18:19:17
|
|
||||||
*/
|
|
||||||
import { html, css, Component, classMap, nextTick, outsideClick } from 'wkit'
|
|
||||||
|
|
||||||
import 'ui/icon/index.js'
|
|
||||||
import 'ui/scroll/index.js'
|
|
||||||
import 'ui/layer/index.js'
|
|
||||||
import 'ui/space/index.js'
|
|
||||||
import 'ui/form/input.js'
|
|
||||||
import 'ui/form/switch.js'
|
|
||||||
import 'ui/form/button.js'
|
|
||||||
import 'ui/form/link.js'
|
|
||||||
|
|
||||||
import './sidebar.js'
|
|
||||||
import './permission.js'
|
|
||||||
import './records.js'
|
|
||||||
|
|
||||||
import { checkPermission, getHistory, saveHosts, noop } from '../utils/index.js'
|
|
||||||
|
|
||||||
const HOST_DATA = await getHistory()
|
|
||||||
|
|
||||||
class Home extends Component {
|
|
||||||
static props = {
|
|
||||||
editDomain: '', // 当前临时要编辑的域名, 即右键菜单选择到的
|
|
||||||
permissionShow: false
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 32px;
|
|
||||||
color: var(--color-dark-1);
|
|
||||||
background: #f0f0f0;
|
|
||||||
}
|
|
||||||
.visible {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noselect {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
|
|
||||||
css`
|
|
||||||
.main {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.main .toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 40px;
|
|
||||||
padding: 0 15px;
|
|
||||||
border-bottom: 1px solid var(--color-plain-3);
|
|
||||||
background: var(--color-plain-1);
|
|
||||||
}
|
|
||||||
.main .list {
|
|
||||||
overflow: hidden;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
|
|
||||||
css`
|
|
||||||
.context-menu {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100px;
|
|
||||||
padding: 5px 0;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-menu .item {
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
padding: 0 15px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-menu .item :hover {
|
|
||||||
background: #f2f5fc;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
]
|
|
||||||
|
|
||||||
async mounted() {
|
|
||||||
let writable = await checkPermission()
|
|
||||||
|
|
||||||
if (writable) {
|
|
||||||
this.$store.HOST_DATA = HOST_DATA
|
|
||||||
this.$store.domains = Object.keys(HOST_DATA)
|
|
||||||
this.$refs.domain.mounted()
|
|
||||||
outsideClick(this.$refs.context, _ => this.$refs.context.close())
|
|
||||||
} else {
|
|
||||||
this.permissionShow = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addRecord() {
|
|
||||||
if (this.$store.activeDomain) {
|
|
||||||
this.$store.records.push({
|
|
||||||
record: '',
|
|
||||||
value: '',
|
|
||||||
enabled: true,
|
|
||||||
remark: ''
|
|
||||||
})
|
|
||||||
nextTick(_ => (this.$refs.list.scrollTop = 1e6))
|
|
||||||
} else {
|
|
||||||
layer.toast('请先选择域名', 'warn')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showMenu(ev) {
|
|
||||||
this.$refs.context.close()
|
|
||||||
var { pageX, pageY } = ev
|
|
||||||
if (pageY + 70 > 600) {
|
|
||||||
pageY -= 70
|
|
||||||
}
|
|
||||||
|
|
||||||
var elem = ev.target
|
|
||||||
|
|
||||||
if (elem.tagName !== 'LI') {
|
|
||||||
elem = elem.parentNode
|
|
||||||
}
|
|
||||||
this.editDomain = elem.dataset.name
|
|
||||||
setTimeout(_ => {
|
|
||||||
this.$refs.context.moveTo({ left: pageX + 'px', top: pageY + 'px' })
|
|
||||||
this.$refs.context.show()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
confirmAction(ev) {
|
|
||||||
this.$refs.context.close()
|
|
||||||
if (ev.target.tagName === 'LI') {
|
|
||||||
let act = ev.target.dataset.act
|
|
||||||
let { HOST_DATA, records, domains } = this.$store
|
|
||||||
let idx = domains.indexOf(this.editDomain)
|
|
||||||
|
|
||||||
if (act === 'del') {
|
|
||||||
layer
|
|
||||||
.confirm(`是否要删除域名「${this.editDomain}」?`)
|
|
||||||
.then(res => {
|
|
||||||
if (this.editDomain === this.$store.activeDomain) {
|
|
||||||
if (records.length) {
|
|
||||||
return layer.toast(
|
|
||||||
'该域名下有主机记录, 请先删除主机记录后再删除域名',
|
|
||||||
'error'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (HOST_DATA[this.editDomain].length > 0) {
|
|
||||||
return layer.toast(
|
|
||||||
'该域名下有主机记录, 请先删除主机记录后再删除域名',
|
|
||||||
'error'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete HOST_DATA[this.editDomain]
|
|
||||||
|
|
||||||
domains.splice(idx, 1)
|
|
||||||
|
|
||||||
this.editDomain = ''
|
|
||||||
this.$store.records = []
|
|
||||||
this.$store.activeDomain = domains[0]
|
|
||||||
this.$refs.domain.mounted()
|
|
||||||
this.save()
|
|
||||||
})
|
|
||||||
.catch(noop)
|
|
||||||
} else if (act === 'edit') {
|
|
||||||
layer
|
|
||||||
.prompt(
|
|
||||||
`请输入新的名字「${this.editDomain}」`,
|
|
||||||
this.editDomain,
|
|
||||||
(val, done) => {
|
|
||||||
if (val === this.editDomain || HOST_DATA[val]) {
|
|
||||||
return layer.toast(`${val} 域名没有变化, 或已经存在`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
val === 'localhost' ||
|
|
||||||
val === 'local' ||
|
|
||||||
/^[\w.]+\.[a-z]+$/.test(val)
|
|
||||||
) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
layer.toast('域名格式错误', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(val => {
|
|
||||||
domains[idx] = val
|
|
||||||
HOST_DATA[val] = HOST_DATA[this.editDomain]
|
|
||||||
delete HOST_DATA[this.editDomain]
|
|
||||||
|
|
||||||
this.$store.activeDomain = val
|
|
||||||
this.editDomain = ''
|
|
||||||
this.$refs.domain.mounted()
|
|
||||||
this.save()
|
|
||||||
})
|
|
||||||
.catch(noop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
save() {
|
|
||||||
let { HOST_DATA, activeDomain, records } = this.$store
|
|
||||||
if (activeDomain) {
|
|
||||||
HOST_DATA[activeDomain] = records.filter(it => it.record && it.value)
|
|
||||||
}
|
|
||||||
saveHosts(HOST_DATA)
|
|
||||||
layer.toast('保存成功', 'success')
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<wc-sidebar
|
|
||||||
ref="domain"
|
|
||||||
@toggle-domain=${_ => (this.$refs.list.scrollTop = 0)}
|
|
||||||
@show-menu=${ev => this.showMenu(ev.event)}
|
|
||||||
@save=${this.save}
|
|
||||||
></wc-sidebar>
|
|
||||||
|
|
||||||
<main class="main noselect">
|
|
||||||
<header class="toolbar">
|
|
||||||
<wc-button size="m" icon="plus" @click=${this.addRecord}
|
|
||||||
>新增记录</wc-button
|
|
||||||
>
|
|
||||||
<wc-button size="m" icon="fly" @click=${this.save}>保存</wc-button>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<wc-records class="list" ref="list"></wc-records>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<wc-permission
|
|
||||||
class=${classMap({ visible: this.permissionShow })}
|
|
||||||
></wc-permission>
|
|
||||||
|
|
||||||
<wc-layer ref="context" left="100px" top="0" radius="0">
|
|
||||||
<ul class="context-menu" @click=${this.confirmAction}>
|
|
||||||
<li class="item" data-act="del">删除域名</li>
|
|
||||||
<li class="item" data-act="edit">修改域名</li>
|
|
||||||
</ul>
|
|
||||||
</wc-layer>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Home.reg('home')
|
|
|
@ -1,109 +0,0 @@
|
||||||
/**
|
|
||||||
* {}
|
|
||||||
* @author yutent<yutent.io@gmail.com>
|
|
||||||
* @date 2023/08/08 18:19:17
|
|
||||||
*/
|
|
||||||
import { html, css, Component } from 'wkit'
|
|
||||||
|
|
||||||
import { checkPermission } from '../utils/index.js'
|
|
||||||
|
|
||||||
const tips_header = `/************************************************************/
|
|
||||||
* hosts文件没有写权限 *
|
|
||||||
/************************************************************/
|
|
||||||
`
|
|
||||||
|
|
||||||
class Permission extends Component {
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.noselect {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.permission-error {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
z-index: 1024;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 24px 52px;
|
|
||||||
line-height: 1.5;
|
|
||||||
background: rgba(255, 233, 233, 0.95);
|
|
||||||
-webkit-backdrop-filter: blur(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
font-size: 14px;
|
|
||||||
color: var(--color-red-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
width: 600px;
|
|
||||||
padding: 0 30px 30px;
|
|
||||||
border: 1px solid var(--color-orange-1);
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend {
|
|
||||||
padding: 0 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
margin-top: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
display: block;
|
|
||||||
padding: 8px 10px;
|
|
||||||
margin-top: 8px;
|
|
||||||
border-left: 3px solid var(--color-plain-3);
|
|
||||||
background: rgba(255, 255, 255, 0.8);
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
async check() {
|
|
||||||
let writable = await checkPermission()
|
|
||||||
if (writable) {
|
|
||||||
location.reload()
|
|
||||||
} else {
|
|
||||||
layer.toast('hosts文件没有写权限, 请按提示修改', 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
|
||||||
<div class="permission-error">
|
|
||||||
<pre class="noselect">${tips_header}</pre>
|
|
||||||
<fieldset>
|
|
||||||
<legend>操作指引</legend>
|
|
||||||
<dl>
|
|
||||||
<dt>MacOS用户</dt>
|
|
||||||
<dd>打开终端, 执行以下命令</dd>
|
|
||||||
<dd><code>sudo chown $USER:admin /etc/hosts</code></dd>
|
|
||||||
|
|
||||||
<dt>Linux用户</dt>
|
|
||||||
<dd>打开终端, 执行以下命令</dd>
|
|
||||||
<dd><code>sudo chown $USER: /etc/hosts</code></dd>
|
|
||||||
|
|
||||||
<dt>完成之后</dt>
|
|
||||||
<dd>点击下面的按钮重新检测.</dd>
|
|
||||||
<dd>
|
|
||||||
<wc-button type="danger" @click=${this.check}>权限检测</wc-button>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Permission.reg('permission')
|
|
|
@ -15,7 +15,7 @@ class Sidebar extends Component {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: var(--color-plain-2);
|
background: var(--color-plain-1);
|
||||||
}
|
}
|
||||||
.noselect {
|
.noselect {
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
|
@ -23,26 +23,28 @@ class Sidebar extends Component {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.domain-list {
|
.navibar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.domain-list .item {
|
.navibar .item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
|
gap: 12px;
|
||||||
|
border-radius: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.15s ease-in-out;
|
transition: background 0.15s ease-in-out;
|
||||||
}
|
|
||||||
|
|
||||||
.item wc-icon {
|
--wc-icon-size: 16px;
|
||||||
--wc-icon-size: 12px;
|
|
||||||
margin-left: 8px;
|
|
||||||
color: var(--color-grey-2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item:hover,
|
.item:hover,
|
||||||
|
@ -50,114 +52,43 @@ class Sidebar extends Component {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.item.active {
|
.item.active {
|
||||||
border-right: 2px solid var(--color-orange-1);
|
color: var(--color-orange-3);
|
||||||
color: var(--color-orange-1);
|
box-shadow: 0 0 6px rgba(0, 0, 0, 0.05);
|
||||||
}
|
|
||||||
.item.active wc-icon {
|
|
||||||
color: var(--color-orange-1);
|
|
||||||
}
|
|
||||||
.item.blank {
|
|
||||||
justify-content: center;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item.blank:hover {
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.action {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 50px;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
addDomain() {
|
switchNavi(id) {
|
||||||
layer
|
let path = '/' + id
|
||||||
.prompt('请输入根域名', function (val, done) {
|
this.$store.navi = id
|
||||||
if (
|
if (id === 'containers') {
|
||||||
val === 'localhost' ||
|
path = '/'
|
||||||
val === 'local' ||
|
|
||||||
/^[\w.\-]+\.[a-z]+$/.test(val)
|
|
||||||
) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
layer.toast('域名格式错误', 'error')
|
|
||||||
}
|
}
|
||||||
})
|
this.$router.push(path)
|
||||||
.then(val => {
|
localStorage.setItem('last_navi', id)
|
||||||
let { HOST_DATA, records } = this.$store
|
|
||||||
this.$store.domains.push(val)
|
|
||||||
HOST_DATA[val] = []
|
|
||||||
if (!this.$store.activeDomain) {
|
|
||||||
this.toggleDomain(null, val)
|
|
||||||
}
|
|
||||||
this.$emit('save')
|
|
||||||
})
|
|
||||||
.catch(noop)
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleDomain(ev, name) {
|
|
||||||
let { HOST_DATA, records } = this.$store
|
|
||||||
name = name ?? ev.currentTarget.dataset.name
|
|
||||||
this.$store.activeDomain = name
|
|
||||||
|
|
||||||
this.$store.records = records = (HOST_DATA[name] || []).sort((a, b) =>
|
|
||||||
a.record.localeCompare(b.record)
|
|
||||||
)
|
|
||||||
|
|
||||||
let tmp_records = Object.create(null)
|
|
||||||
for (let it of records) {
|
|
||||||
if (tmp_records[it.record]) {
|
|
||||||
tmp_records[it.record].push(it)
|
|
||||||
} else {
|
|
||||||
tmp_records[it.record] = [it]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$store.tmp_records = tmp_records
|
|
||||||
|
|
||||||
document.title = `伪域名解析 ${name} `
|
|
||||||
localStorage.setItem('last_domain', name)
|
|
||||||
nextTick(() => {
|
|
||||||
this.$emit('toggle-domain')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.toggleDomain(null, this.$store.activeDomain)
|
this.switchNavi(this.$store.navi)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<wc-scroll class="domain-list noselect">
|
<menu class="navibar">
|
||||||
<ul
|
${this.$store.menus.map(
|
||||||
@contextmenu.prevent=${ev => this.$emit('show-menu', { event: ev })}
|
|
||||||
>
|
|
||||||
${this.$store.domains.map(
|
|
||||||
it => html`
|
it => html`
|
||||||
<li
|
<li
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
item: true,
|
item: true,
|
||||||
active: it === this.$store.activeDomain
|
active: it.id === this.$store.navi
|
||||||
})}
|
})}
|
||||||
data-name=${it}
|
@click=${ev => this.switchNavi(it.id)}
|
||||||
@click=${this.toggleDomain}
|
|
||||||
>
|
>
|
||||||
<span>${it}</span>
|
<wc-icon name=${it.icon}></wc-icon>
|
||||||
<wc-icon name="right"></wc-icon>
|
<span>${it.name}</span>
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
${this.$store.domains.length < 1
|
</menu>
|
||||||
? html`<li class="item blank">没有域名</li>`
|
|
||||||
: ''}
|
|
||||||
</ul>
|
|
||||||
</wc-scroll>
|
|
||||||
<section class="action">
|
|
||||||
<wc-button circle icon="plus" @click=${this.addDomain}> </wc-button>
|
|
||||||
</section>
|
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* {}
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2024/01/11 17:42:40
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createRouter, createWebHistory } from 'wkitd'
|
||||||
|
|
||||||
|
import './views/home.js'
|
||||||
|
import './views/images.js'
|
||||||
|
import './views/volumes.js'
|
||||||
|
|
||||||
|
export default createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'wc-containers'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/images',
|
||||||
|
name: 'wc-images'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/volumes',
|
||||||
|
name: 'wc-volumes'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
|
@ -7,9 +7,22 @@
|
||||||
import { createStore } from 'wkitd'
|
import { createStore } from 'wkitd'
|
||||||
|
|
||||||
export default createStore({
|
export default createStore({
|
||||||
HOST_DATA: {},
|
navi: localStorage.getItem('last_navi') || 'containers',
|
||||||
activeDomain: localStorage.getItem('last_domain') || '', //当前选中的域名
|
menus: [
|
||||||
domains: [],
|
{
|
||||||
records: [],
|
id: 'containers',
|
||||||
tmp_records: []
|
name: '容器管理',
|
||||||
|
icon: 'layout'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'images',
|
||||||
|
name: '镜像管理',
|
||||||
|
icon: 'menu'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'volumes',
|
||||||
|
name: '磁盘管理',
|
||||||
|
icon: 'pie'
|
||||||
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,95 +6,10 @@
|
||||||
|
|
||||||
import { nextTick } from 'wkit'
|
import { nextTick } from 'wkit'
|
||||||
|
|
||||||
const APP_CONFIG_DIR = `${native.env.CONFIG_DIR}/hosts-switch`
|
|
||||||
const HOST_FILE = `${APP_CONFIG_DIR}/host.cache`
|
|
||||||
const LOCK_FILE = `${APP_CONFIG_DIR}/lock`
|
|
||||||
|
|
||||||
export function noop() {}
|
export function noop() {}
|
||||||
|
|
||||||
export function checkPermission() {
|
export function checkPermission() {}
|
||||||
return native.fs.access('/etc/hosts', 'a+')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getHistory() {
|
export async function getHistory() {}
|
||||||
if (await native.fs.isfile(LOCK_FILE)) {
|
|
||||||
let cache = await native.fs.read(HOST_FILE)
|
|
||||||
return JSON.parse(cache)
|
|
||||||
}
|
|
||||||
|
|
||||||
let cache = await native.fs.read('/etc/hosts')
|
export function saveHosts(dict) {}
|
||||||
let records = cache.split(/[\n\r]+/)
|
|
||||||
let list = []
|
|
||||||
let dict = {}
|
|
||||||
|
|
||||||
records.forEach(str => {
|
|
||||||
str = str.trim()
|
|
||||||
let matches = str.match(/^(#*?)\s*(\d+\.\d+\.\d+\.\d+)\s+(.*)/)
|
|
||||||
|
|
||||||
if (matches) {
|
|
||||||
let names = matches[3].split(/\s+/).map(it => it.trim())
|
|
||||||
let name
|
|
||||||
while ((name = names.pop())) {
|
|
||||||
list.push({ ip: matches[2], enabled: !matches[1], name })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
records = null
|
|
||||||
|
|
||||||
list.forEach(it => {
|
|
||||||
it.name = it.name.split('.')
|
|
||||||
let domain = it.name.splice(-2, 2).join('.')
|
|
||||||
if (domain === 'com.cn' || domain === 'org.cn' || domain === 'net.cn') {
|
|
||||||
domain = it.name.pop() + '.' + domain
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dict[domain]) {
|
|
||||||
dict[domain].push({
|
|
||||||
value: it.ip,
|
|
||||||
enabled: it.enabled,
|
|
||||||
record: it.name.join('.') || '@',
|
|
||||||
remark: ''
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
dict[domain] = [
|
|
||||||
{
|
|
||||||
value: it.ip,
|
|
||||||
enabled: it.enabled,
|
|
||||||
record: it.name.join('.') || '@',
|
|
||||||
remark: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
list = null
|
|
||||||
try {
|
|
||||||
await native.fs.write(HOST_FILE, JSON.stringify(dict))
|
|
||||||
await native.fs.write(LOCK_FILE, '')
|
|
||||||
} catch (err) {}
|
|
||||||
return dict
|
|
||||||
}
|
|
||||||
|
|
||||||
export function saveHosts(dict) {
|
|
||||||
nextTick(async () => {
|
|
||||||
var txt = ''
|
|
||||||
for (let k in dict) {
|
|
||||||
for (let it of dict[k]) {
|
|
||||||
if (it.enabled) {
|
|
||||||
var name = it.record === '@' ? '' : it.record
|
|
||||||
if (name) {
|
|
||||||
name += '.'
|
|
||||||
}
|
|
||||||
txt += `${it.value.padEnd(15, ' ')} ${name + k}\n`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
txt += '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await native.fs.write(HOST_FILE, JSON.stringify(dict))
|
|
||||||
await native.fs.write('/etc/hosts', txt)
|
|
||||||
} catch (err) {
|
|
||||||
layer.alert(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
/**
|
||||||
|
* {}
|
||||||
|
* @author yutent<yutent.io@gmail.com>
|
||||||
|
* @date 2023/08/08 18:19:17
|
||||||
|
*/
|
||||||
|
import { html, css, Component, classMap, nextTick, outsideClick } from 'wkit'
|
||||||
|
|
||||||
|
// import '../components/records.js'
|
||||||
|
|
||||||
|
import { noop } from '../utils/index.js'
|
||||||
|
|
||||||
|
class Home extends Component {
|
||||||
|
static props = {}
|
||||||
|
|
||||||
|
static styles = [
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
color: var(--color-dark-1);
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noselect {
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
|
||||||
|
css`
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.main .toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 48px;
|
||||||
|
border-bottom: 1px solid var(--color-plain-3);
|
||||||
|
}
|
||||||
|
.main .list {
|
||||||
|
overflow: hidden;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
|
||||||
|
css`
|
||||||
|
.context-menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100px;
|
||||||
|
padding: 5px 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu .item {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
padding: 0 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-menu .item :hover {
|
||||||
|
background: #f2f5fc;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
]
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// outsideClick(this.$refs.context, _ => this.$refs.context.close())
|
||||||
|
}
|
||||||
|
|
||||||
|
showMenu(ev) {
|
||||||
|
this.$refs.context.close()
|
||||||
|
var { pageX, pageY } = ev
|
||||||
|
if (pageY + 70 > 600) {
|
||||||
|
pageY -= 70
|
||||||
|
}
|
||||||
|
|
||||||
|
var elem = ev.target
|
||||||
|
|
||||||
|
if (elem.tagName !== 'LI') {
|
||||||
|
elem = elem.parentNode
|
||||||
|
}
|
||||||
|
this.editDomain = elem.dataset.name
|
||||||
|
setTimeout(_ => {
|
||||||
|
this.$refs.context.moveTo({ left: pageX + 'px', top: pageY + 'px' })
|
||||||
|
this.$refs.context.show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmAction(ev) {
|
||||||
|
this.$refs.context.close()
|
||||||
|
if (ev.target.tagName === 'LI') {
|
||||||
|
let act = ev.target.dataset.act
|
||||||
|
if (act === 'del') {
|
||||||
|
} else if (act === 'edit') {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
let { HOST_DATA, activeDomain, records } = this.$store
|
||||||
|
if (activeDomain) {
|
||||||
|
HOST_DATA[activeDomain] = records.filter(it => it.record && it.value)
|
||||||
|
}
|
||||||
|
saveHosts(HOST_DATA)
|
||||||
|
layer.toast('保存成功', 'success')
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<main class="main noselect">
|
||||||
|
<header class="toolbar">
|
||||||
|
<wc-input round></wc-input>
|
||||||
|
</header>
|
||||||
|
</main>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Home.reg('containers')
|
Loading…
Reference in New Issue