parent
7e93319873
commit
5e9cc09fcb
11 changed files with 271 additions and 5 deletions
|
@ -45,7 +45,7 @@ gulp.task('build:copy:locales', cb => {
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build:client:script', () => {
|
gulp.task('build:client:script', () => {
|
||||||
return gulp.src(['./src/server/web/boot.js'])
|
return gulp.src(['./src/server/web/boot.js', './src/server/web/bios.js', './src/server/web/cli.js'])
|
||||||
.pipe(replace('VERSION', JSON.stringify(meta.version)))
|
.pipe(replace('VERSION', JSON.stringify(meta.version)))
|
||||||
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
|
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
|
||||||
.pipe(terser({
|
.pipe(terser({
|
||||||
|
@ -55,7 +55,7 @@ gulp.task('build:client:script', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build:client:style', () => {
|
gulp.task('build:client:style', () => {
|
||||||
return gulp.src(['./src/server/web/style.css'])
|
return gulp.src(['./src/server/web/style.css', './src/server/web/bios.css', './src/server/web/cli.css'])
|
||||||
.pipe(cssnano())
|
.pipe(cssnano())
|
||||||
.pipe(gulp.dest('./built/server/web/'));
|
.pipe(gulp.dest('./built/server/web/'));
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<Fa :icon="faExternalLinkAlt" class="icon"/>
|
<Fa :icon="faExternalLinkAlt" class="icon"/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<MkA class="main _button _formPanel _formClickable" :class="{ active }" :to="to" v-else>
|
<MkA class="main _button _formPanel _formClickable" :class="{ active }" :to="to" :behavior="behavior" v-else>
|
||||||
<span class="icon"><slot name="icon"></slot></span>
|
<span class="icon"><slot name="icon"></slot></span>
|
||||||
<span class="text"><slot></slot></span>
|
<span class="text"><slot></slot></span>
|
||||||
<span class="right">
|
<span class="right">
|
||||||
|
@ -38,6 +38,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
|
behavior: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -98,6 +98,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
nav() {
|
nav() {
|
||||||
|
if (this.behavior === 'browser') {
|
||||||
|
location.href = this.to;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.to.startsWith('/my/messaging')) {
|
if (this.to.startsWith('/my/messaging')) {
|
||||||
if (ColdDeviceStorage.get('chatOpenBehavior') === 'window') return this.window();
|
if (ColdDeviceStorage.get('chatOpenBehavior') === 'window') return this.window();
|
||||||
if (ColdDeviceStorage.get('chatOpenBehavior') === 'popout') return this.popout();
|
if (ColdDeviceStorage.get('chatOpenBehavior') === 'popout') return this.popout();
|
||||||
|
|
|
@ -23,13 +23,16 @@
|
||||||
|
|
||||||
<FormLink to="/settings/registry"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.registry }}</FormLink>
|
<FormLink to="/settings/registry"><template #icon><Fa :icon="faCogs"/></template>{{ $ts.registry }}</FormLink>
|
||||||
|
|
||||||
|
<FormLink to="/bios" behavior="browser"><template #icon><Fa :icon="faDoorOpen"/></template>BIOS</FormLink>
|
||||||
|
<FormLink to="/cli" behavior="browser"><template #icon><Fa :icon="faDoorOpen"/></template>CLI</FormLink>
|
||||||
|
|
||||||
<FormButton @click="closeAccount" danger>{{ $ts.closeAccount }}</FormButton>
|
<FormButton @click="closeAccount" danger>{{ $ts.closeAccount }}</FormButton>
|
||||||
</FormBase>
|
</FormBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineAsyncComponent, defineComponent } from 'vue';
|
import { defineAsyncComponent, defineComponent } from 'vue';
|
||||||
import { faEllipsisH, faCogs } from '@fortawesome/free-solid-svg-icons';
|
import { faEllipsisH, faCogs, faDoorOpen } from '@fortawesome/free-solid-svg-icons';
|
||||||
import FormSwitch from '@/components/form/switch.vue';
|
import FormSwitch from '@/components/form/switch.vue';
|
||||||
import FormSelect from '@/components/form/select.vue';
|
import FormSelect from '@/components/form/select.vue';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
|
@ -61,7 +64,7 @@ export default defineComponent({
|
||||||
icon: faEllipsisH
|
icon: faEllipsisH
|
||||||
},
|
},
|
||||||
debug,
|
debug,
|
||||||
faCogs
|
faCogs, faDoorOpen,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
40
src/server/web/bios.css
Normal file
40
src/server/web/bios.css
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
* {
|
||||||
|
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background: #ffb4e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background: #dedede;
|
||||||
|
}
|
||||||
|
main > .tabs {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: solid 4px #c3c3c3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#lsEditor > .adder {
|
||||||
|
margin: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
border: solid 2px #c3c3c3;
|
||||||
|
}
|
||||||
|
#lsEditor > .adder > textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 5em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#lsEditor > .record {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: solid 1px #c3c3c3;
|
||||||
|
}
|
||||||
|
#lsEditor > .record > header {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
#lsEditor > .record > textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 5em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
87
src/server/web/bios.js
Normal file
87
src/server/web/bios.js
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.onload = async () => {
|
||||||
|
const account = JSON.parse(localStorage.getItem('account'));
|
||||||
|
const i = account.token;
|
||||||
|
|
||||||
|
const api = (endpoint, data = {}) => {
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
// Append a credential
|
||||||
|
if (i) data.i = i;
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: 'omit',
|
||||||
|
cache: 'no-cache'
|
||||||
|
}).then(async (res) => {
|
||||||
|
const body = res.status === 204 ? null : await res.json();
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
resolve(body);
|
||||||
|
} else if (res.status === 204) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(body.error);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = document.getElementById('content');
|
||||||
|
|
||||||
|
document.getElementById('ls').addEventListener('click', () => {
|
||||||
|
content.innerHTML = '';
|
||||||
|
|
||||||
|
const lsEditor = document.createElement('div');
|
||||||
|
lsEditor.id = 'lsEditor';
|
||||||
|
|
||||||
|
const adder = document.createElement('div');
|
||||||
|
adder.classList.add('adder');
|
||||||
|
const addKeyInput = document.createElement('input');
|
||||||
|
const addValueTextarea = document.createElement('textarea');
|
||||||
|
const addButton = document.createElement('button');
|
||||||
|
addButton.textContent = 'add';
|
||||||
|
addButton.addEventListener('click', () => {
|
||||||
|
localStorage.setItem(addKeyInput.value, addValueTextarea.value);
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
adder.appendChild(addKeyInput);
|
||||||
|
adder.appendChild(addValueTextarea);
|
||||||
|
adder.appendChild(addButton);
|
||||||
|
lsEditor.appendChild(adder);
|
||||||
|
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const k = localStorage.key(i);
|
||||||
|
const record = document.createElement('div');
|
||||||
|
record.classList.add('record');
|
||||||
|
const header = document.createElement('header');
|
||||||
|
header.textContent = k;
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.textContent = localStorage.getItem(k);
|
||||||
|
const saveButton = document.createElement('button');
|
||||||
|
saveButton.textContent = 'save';
|
||||||
|
saveButton.addEventListener('click', () => {
|
||||||
|
localStorage.setItem(k, textarea.value);
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
const removeButton = document.createElement('button');
|
||||||
|
removeButton.textContent = 'remove';
|
||||||
|
removeButton.addEventListener('click', () => {
|
||||||
|
localStorage.removeItem(k);
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
record.appendChild(header);
|
||||||
|
record.appendChild(textarea);
|
||||||
|
record.appendChild(saveButton);
|
||||||
|
record.appendChild(removeButton);
|
||||||
|
lsEditor.appendChild(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
content.appendChild(lsEditor);
|
||||||
|
});
|
||||||
|
};
|
19
src/server/web/cli.css
Normal file
19
src/server/web/cli.css
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
* {
|
||||||
|
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background: #ffb4e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background: #dedede;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tl > div {
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: solid 1px #c3c3c3;
|
||||||
|
}
|
||||||
|
#tl > div > header {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
55
src/server/web/cli.js
Normal file
55
src/server/web/cli.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.onload = async () => {
|
||||||
|
const account = JSON.parse(localStorage.getItem('account'));
|
||||||
|
const i = account.token;
|
||||||
|
|
||||||
|
const api = (endpoint, data = {}) => {
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
// Append a credential
|
||||||
|
if (i) data.i = i;
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
credentials: 'omit',
|
||||||
|
cache: 'no-cache'
|
||||||
|
}).then(async (res) => {
|
||||||
|
const body = res.status === 204 ? null : await res.json();
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
resolve(body);
|
||||||
|
} else if (res.status === 204) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject(body.error);
|
||||||
|
}
|
||||||
|
}).catch(reject);
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('submit').addEventListener('click', () => {
|
||||||
|
api('notes/create', {
|
||||||
|
text: document.getElementById('text').value
|
||||||
|
}).then(() => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
api('notes/timeline').then(notes => {
|
||||||
|
const tl = document.getElementById('tl');
|
||||||
|
for (const note of notes) {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
const name = document.createElement('header');
|
||||||
|
name.textContent = `${note.user.name} @${note.user.username}`;
|
||||||
|
const text = document.createElement('div');
|
||||||
|
text.textContent = `${note.text}`;
|
||||||
|
el.appendChild(name);
|
||||||
|
el.appendChild(text);
|
||||||
|
tl.appendChild(el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -376,6 +376,18 @@ router.get('/info', async ctx => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get('/bios', async ctx => {
|
||||||
|
await ctx.render('bios', {
|
||||||
|
version: config.version,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/cli', async ctx => {
|
||||||
|
await ctx.render('cli', {
|
||||||
|
version: config.version,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const override = (source: string, target: string, depth: number = 0) =>
|
const override = (source: string, target: string, depth: number = 0) =>
|
||||||
[, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/');
|
[, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/');
|
||||||
|
|
||||||
|
|
20
src/server/web/views/bios.pug
Normal file
20
src/server/web/views/bios.pug
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
doctype html
|
||||||
|
|
||||||
|
html
|
||||||
|
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
meta(name='application-name' content='Misskey')
|
||||||
|
title Misskey BIOS
|
||||||
|
style
|
||||||
|
include ../bios.css
|
||||||
|
script
|
||||||
|
include ../bios.js
|
||||||
|
|
||||||
|
body
|
||||||
|
header
|
||||||
|
h1 Misskey BIOS #{version}
|
||||||
|
main
|
||||||
|
div.tabs
|
||||||
|
button#ls edit local storage
|
||||||
|
div#content
|
21
src/server/web/views/cli.pug
Normal file
21
src/server/web/views/cli.pug
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
doctype html
|
||||||
|
|
||||||
|
html
|
||||||
|
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
meta(name='application-name' content='Misskey')
|
||||||
|
title Misskey Cli
|
||||||
|
style
|
||||||
|
include ../cli.css
|
||||||
|
script
|
||||||
|
include ../cli.js
|
||||||
|
|
||||||
|
body
|
||||||
|
header
|
||||||
|
h1 Misskey Cli #{version}
|
||||||
|
main
|
||||||
|
div#form
|
||||||
|
textarea#text
|
||||||
|
button#submit submit
|
||||||
|
div#tl
|
Loading…
Reference in a new issue