diff --git a/usr/lib/dooke/dooke.py b/usr/lib/dooke/dooke.py
index 555b584..20d03ce 100755
--- a/usr/lib/dooke/dooke.py
+++ b/usr/lib/dooke/dooke.py
@@ -14,11 +14,6 @@ __dir__ = os.path.dirname(os.path.realpath(__file__))
web_root = os.path.join(__dir__, './webapp')
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):
diff --git a/usr/lib/dooke/webapp/app.js b/usr/lib/dooke/webapp/app.js
index 7ff2e36..aefcdcd 100644
--- a/usr/lib/dooke/webapp/app.js
+++ b/usr/lib/dooke/webapp/app.js
@@ -6,10 +6,21 @@
import 'es.shim'
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 './components/home.js'
+
+import '../components/sidebar.js'
import { noop } from './utils/index.js'
@@ -26,6 +37,11 @@ createApp({
width: 100%;
height: 100vh;
}
+
+ router-view {
+ flex: 1;
+ height: 100;
+ }
`
],
methods: {
@@ -34,8 +50,18 @@ createApp({
}
},
render() {
- return html` `
+ return html`
+
+
+
+
+
+ `
}
})
+ .use(router)
.use(store)
.mount()
diff --git a/usr/lib/dooke/webapp/components/home.js b/usr/lib/dooke/webapp/components/home.js
deleted file mode 100644
index 68fbf28..0000000
--- a/usr/lib/dooke/webapp/components/home.js
+++ /dev/null
@@ -1,262 +0,0 @@
-/**
- * {}
- * @author yutent
- * @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`
- (this.$refs.list.scrollTop = 0)}
- @show-menu=${ev => this.showMenu(ev.event)}
- @save=${this.save}
- >
-
-
-
-
-
-
-
-
-
-
-
-
- `
- }
-}
-
-Home.reg('home')
diff --git a/usr/lib/dooke/webapp/components/permission.js b/usr/lib/dooke/webapp/components/permission.js
deleted file mode 100644
index 8393252..0000000
--- a/usr/lib/dooke/webapp/components/permission.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * {}
- * @author yutent
- * @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`
-
-
${tips_header}
-
-
- `
- }
-}
-
-Permission.reg('permission')
diff --git a/usr/lib/dooke/webapp/components/sidebar.js b/usr/lib/dooke/webapp/components/sidebar.js
index 08783af..0663c18 100644
--- a/usr/lib/dooke/webapp/components/sidebar.js
+++ b/usr/lib/dooke/webapp/components/sidebar.js
@@ -15,7 +15,7 @@ class Sidebar extends Component {
flex-direction: column;
width: 180px;
height: 100vh;
- background: var(--color-plain-2);
+ background: var(--color-plain-1);
}
.noselect {
-webkit-touch-callout: none;
@@ -23,26 +23,28 @@ class Sidebar extends Component {
user-select: none;
}
- .domain-list {
+ .navibar {
overflow: hidden;
flex: 1;
+ display: flex;
+ flex-direction: column;
width: 100%;
+ gap: 8px;
+ padding: 16px;
}
- .domain-list .item {
+ .navibar .item {
display: flex;
- justify-content: flex-end;
+ justify-content: center;
align-items: center;
height: 40px;
padding: 0 12px;
+ gap: 12px;
+ border-radius: 20px;
cursor: pointer;
transition: background 0.15s ease-in-out;
- }
- .item wc-icon {
- --wc-icon-size: 12px;
- margin-left: 8px;
- color: var(--color-grey-2);
+ --wc-icon-size: 16px;
}
.item:hover,
@@ -50,114 +52,43 @@ class Sidebar extends Component {
background: #fff;
}
.item.active {
- border-right: 2px solid var(--color-orange-1);
- color: var(--color-orange-1);
- }
- .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;
+ color: var(--color-orange-3);
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.05);
}
`
- addDomain() {
- layer
- .prompt('请输入根域名', function (val, done) {
- if (
- val === 'localhost' ||
- val === 'local' ||
- /^[\w.\-]+\.[a-z]+$/.test(val)
- ) {
- done()
- } else {
- layer.toast('域名格式错误', 'error')
- }
- })
- .then(val => {
- 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]
- }
+ switchNavi(id) {
+ let path = '/' + id
+ this.$store.navi = id
+ if (id === 'containers') {
+ path = '/'
}
- this.$store.tmp_records = tmp_records
-
- document.title = `伪域名解析 ${name} `
- localStorage.setItem('last_domain', name)
- nextTick(() => {
- this.$emit('toggle-domain')
- })
+ this.$router.push(path)
+ localStorage.setItem('last_navi', id)
}
mounted() {
- this.toggleDomain(null, this.$store.activeDomain)
+ this.switchNavi(this.$store.navi)
}
render() {
return html`
-
- this.$emit('show-menu', { event: ev })}
- >
- ${this.$store.domains.map(
- it => html`
- -
- ${it}
-
-
- `
- )}
- ${this.$store.domains.length < 1
- ? html`- 没有域名
`
- : ''}
-
-
-
+
`
}
}
diff --git a/usr/lib/dooke/webapp/router.js b/usr/lib/dooke/webapp/router.js
new file mode 100644
index 0000000..8306cf0
--- /dev/null
+++ b/usr/lib/dooke/webapp/router.js
@@ -0,0 +1,29 @@
+/**
+ * {}
+ * @author yutent
+ * @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'
+ }
+ ]
+})
diff --git a/usr/lib/dooke/webapp/store.js b/usr/lib/dooke/webapp/store.js
index 5ff3f17..2dadfd9 100644
--- a/usr/lib/dooke/webapp/store.js
+++ b/usr/lib/dooke/webapp/store.js
@@ -7,9 +7,22 @@
import { createStore } from 'wkitd'
export default createStore({
- HOST_DATA: {},
- activeDomain: localStorage.getItem('last_domain') || '', //当前选中的域名
- domains: [],
- records: [],
- tmp_records: []
+ navi: localStorage.getItem('last_navi') || 'containers',
+ menus: [
+ {
+ id: 'containers',
+ name: '容器管理',
+ icon: 'layout'
+ },
+ {
+ id: 'images',
+ name: '镜像管理',
+ icon: 'menu'
+ },
+ {
+ id: 'volumes',
+ name: '磁盘管理',
+ icon: 'pie'
+ }
+ ]
})
diff --git a/usr/lib/dooke/webapp/utils/index.js b/usr/lib/dooke/webapp/utils/index.js
index 3e3fc21..57c34e8 100644
--- a/usr/lib/dooke/webapp/utils/index.js
+++ b/usr/lib/dooke/webapp/utils/index.js
@@ -6,95 +6,10 @@
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 checkPermission() {
- return native.fs.access('/etc/hosts', 'a+')
-}
+export function checkPermission() {}
-export async function getHistory() {
- if (await native.fs.isfile(LOCK_FILE)) {
- let cache = await native.fs.read(HOST_FILE)
- return JSON.parse(cache)
- }
+export async function getHistory() {}
- let cache = await native.fs.read('/etc/hosts')
- 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)
- }
- })
-}
+export function saveHosts(dict) {}
diff --git a/usr/lib/dooke/webapp/views/home.js b/usr/lib/dooke/webapp/views/home.js
new file mode 100644
index 0000000..7b7e34f
--- /dev/null
+++ b/usr/lib/dooke/webapp/views/home.js
@@ -0,0 +1,133 @@
+/**
+ * {}
+ * @author yutent
+ * @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`
+
+
+
+ `
+ }
+}
+
+Home.reg('containers')
diff --git a/usr/lib/dooke/webapp/views/images.js b/usr/lib/dooke/webapp/views/images.js
new file mode 100644
index 0000000..e69de29
diff --git a/usr/lib/dooke/webapp/views/volumes.js b/usr/lib/dooke/webapp/views/volumes.js
new file mode 100644
index 0000000..e69de29