Compare commits
12 Commits
7fc1dde664
...
d10b3cde72
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d10b3cde72 | ||
|
|
a316eef807 | ||
|
|
f48b6567cd | ||
|
|
3c7d7e6b29 | ||
|
|
7bbdc25ac0 | ||
|
|
7300c091fd | ||
|
|
f7bd8bdd36 | ||
|
|
ffb8c555c5 | ||
|
|
3e1f9c055d | ||
|
|
7a9a9d628a | ||
|
|
70af48db03 | ||
|
|
ac040f20c6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,4 +4,3 @@ thewhitesilk_config.json
|
|||||||
thewhitesilk_data/
|
thewhitesilk_data/
|
||||||
|
|
||||||
deno.lock
|
deno.lock
|
||||||
node_modules/
|
|
||||||
43
client/Data.ts
Normal file
43
client/Data.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import CryptoJS from "./types/CryptoJS.d.ts"
|
||||||
|
const dataIsEmpty = !localStorage.tws_data || localStorage.tws_data == ''
|
||||||
|
|
||||||
|
const aes = {
|
||||||
|
enc: (m: string, k: string) => CryptoJS.AES.encrypt(m, k).toString(),
|
||||||
|
dec: (m: string, k: string) => CryptoJS.AES.decrypt(m, k).toString(CryptoJS.enc.Utf8),
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = location.host + '_TWS_姐姐'
|
||||||
|
|
||||||
|
if (dataIsEmpty) localStorage.tws_data = aes.enc('{}', key)
|
||||||
|
|
||||||
|
let _dec = aes.dec(localStorage.tws_data, key)
|
||||||
|
if (_dec == '') _dec = '{}'
|
||||||
|
|
||||||
|
const _data_cached = JSON.parse(_dec)
|
||||||
|
|
||||||
|
// 類型定義
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
data: {
|
||||||
|
apply: () => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
(window.data == null) && (window.data = new Proxy({
|
||||||
|
apply() {
|
||||||
|
localStorage.tws_data = aes.enc(JSON.stringify(_data_cached), key)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
get(_obj, k) {
|
||||||
|
return _data_cached[k]
|
||||||
|
},
|
||||||
|
set(_obj, k, v) {
|
||||||
|
_data_cached[k] = v
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// deno-lint-ignore no-window
|
||||||
|
export default window.data
|
||||||
11
client/api/Client.ts
Normal file
11
client/api/Client.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { io, Socket } from 'https://unpkg.com/socket.io-client@4.8.1/dist/socket.io.esm.min.js'
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
static socket: Socket
|
||||||
|
static connect() {
|
||||||
|
this.socket && this.socket.disconnect()
|
||||||
|
this.socket = io()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Client
|
||||||
113
client/types/CryptoJS.d.ts
vendored
Normal file
113
client/types/CryptoJS.d.ts
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// https://github.com/nozzlegear/crypto-js.d.ts/blob/master/crypto-js.d.ts
|
||||||
|
// Forked from https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/9143f1233f13692c14ae2afe7aee2d5014cba916/crypto-js/crypto-js.d.ts
|
||||||
|
|
||||||
|
declare namespace CryptoJS {
|
||||||
|
type Hash = (message: string, key?: string, ...options: any[]) => string;
|
||||||
|
interface Cipher {
|
||||||
|
encrypt(message: string, secretPassphrase: string, option?: CipherOption): WordArray;
|
||||||
|
decrypt(encryptedMessage: string | WordArray, secretPassphrase: string, option?: CipherOption): DecryptedMessage;
|
||||||
|
}
|
||||||
|
interface CipherAlgorythm {
|
||||||
|
createEncryptor(secretPassphrase: string, option?: CipherOption): Encriptor;
|
||||||
|
createDecryptor(secretPassphrase: string, option?: CipherOption): Decryptor;
|
||||||
|
}
|
||||||
|
interface Encriptor {
|
||||||
|
process(messagePart: string): string;
|
||||||
|
finalize(): string;
|
||||||
|
}
|
||||||
|
interface Decryptor {
|
||||||
|
process(messagePart: string): string;
|
||||||
|
finalize(): string;
|
||||||
|
}
|
||||||
|
interface WordArray {
|
||||||
|
iv: string;
|
||||||
|
salt: string;
|
||||||
|
ciphertext: string;
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
type DecryptedMessage = {
|
||||||
|
toString(encoder?: Encoder): string;
|
||||||
|
};
|
||||||
|
interface CipherOption {
|
||||||
|
iv?: string;
|
||||||
|
mode?: Mode;
|
||||||
|
padding?: Padding;
|
||||||
|
[option: string]: any;
|
||||||
|
}
|
||||||
|
interface Encoder {
|
||||||
|
parse(encodedMessage: string): any;
|
||||||
|
stringify(words: any): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Mode {}
|
||||||
|
interface Padding {}
|
||||||
|
|
||||||
|
interface Hashes {
|
||||||
|
MD5: Hash;
|
||||||
|
SHA1: Hash;
|
||||||
|
SHA256: Hash;
|
||||||
|
SHA224: Hash;
|
||||||
|
SHA512: Hash;
|
||||||
|
SHA384: Hash;
|
||||||
|
SHA3: Hash;
|
||||||
|
RIPEMD160: Hash;
|
||||||
|
HmacMD5: Hash;
|
||||||
|
HmacSHA1: Hash;
|
||||||
|
HmacSHA256: Hash;
|
||||||
|
HmacSHA224: Hash;
|
||||||
|
HmacSHA512: Hash;
|
||||||
|
HmacSHA384: Hash;
|
||||||
|
HmacSHA3: Hash;
|
||||||
|
HmacRIPEMD160: Hash;
|
||||||
|
PBKDF2: Hash;
|
||||||
|
AES: Cipher;
|
||||||
|
DES: Cipher;
|
||||||
|
TripleDES: Cipher;
|
||||||
|
RC4: Cipher;
|
||||||
|
RC4Drop: Cipher;
|
||||||
|
Rabbit: Cipher;
|
||||||
|
RabbitLegacy: Cipher;
|
||||||
|
EvpKDF: Cipher;
|
||||||
|
algo: {
|
||||||
|
AES: CipherAlgorythm;
|
||||||
|
DES: CipherAlgorythm;
|
||||||
|
TrippleDES: CipherAlgorythm;
|
||||||
|
RC4: CipherAlgorythm;
|
||||||
|
RC4Drop: CipherAlgorythm;
|
||||||
|
Rabbit: CipherAlgorythm;
|
||||||
|
RabbitLegacy: CipherAlgorythm;
|
||||||
|
EvpKDF: CipherAlgorythm;
|
||||||
|
};
|
||||||
|
format: {
|
||||||
|
OpenSSL: any;
|
||||||
|
Hex: any;
|
||||||
|
};
|
||||||
|
enc: {
|
||||||
|
Latin1: Encoder;
|
||||||
|
Utf8: Encoder;
|
||||||
|
Hex: Encoder;
|
||||||
|
Utf16: Encoder;
|
||||||
|
Utf16LE: Encoder;
|
||||||
|
Base64: Encoder;
|
||||||
|
};
|
||||||
|
mode: {
|
||||||
|
CFB: Mode;
|
||||||
|
CTR: Mode;
|
||||||
|
CTRGladman: Mode;
|
||||||
|
OFB: Mode;
|
||||||
|
ECB: Mode;
|
||||||
|
};
|
||||||
|
pad: {
|
||||||
|
Pkcs7: Padding;
|
||||||
|
AnsiX923: Padding;
|
||||||
|
Iso10126: Padding;
|
||||||
|
Iso97971: Padding;
|
||||||
|
ZeroPadding: Padding;
|
||||||
|
NoPadding: Padding;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare let CryptoJS: CryptoJS.Hashes;
|
||||||
|
|
||||||
|
export default CryptoJS
|
||||||
@@ -18,57 +18,9 @@ export default function App() {
|
|||||||
nickName: "Maya Fey",
|
nickName: "Maya Fey",
|
||||||
content: "我是绫里真宵, 是一名灵媒师~"
|
content: "我是绫里真宵, 是一名灵媒师~"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "麻油衣酱",
|
|
||||||
content: "成步堂君, 我又坐牢了("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "Maya Fey",
|
|
||||||
content: "我是绫里真宵, 是一名灵媒师~"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "麻油衣酱",
|
|
||||||
content: "成步堂君, 我又坐牢了("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "Maya Fey",
|
|
||||||
content: "我是绫里真宵, 是一名灵媒师~"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "麻油衣酱",
|
|
||||||
content: "成步堂君, 我又坐牢了("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "Maya Fey",
|
|
||||||
content: "我是绫里真宵, 是一名灵媒师~"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "麻油衣酱",
|
|
||||||
content: "成步堂君, 我又坐牢了("
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "Maya Fey",
|
|
||||||
content: "我是绫里真宵, 是一名灵媒师~"
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
const [contactsMap, setContactsMap] = React.useState({
|
const [contactsMap, setContactsMap] = React.useState({
|
||||||
默认分组: [
|
所有: [
|
||||||
{
|
{
|
||||||
userId: 0,
|
userId: 0,
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
||||||
@@ -78,19 +30,7 @@ export default function App() {
|
|||||||
userId: 0,
|
userId: 0,
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
||||||
nickName: "Maya Fey",
|
nickName: "Maya Fey",
|
||||||
}
|
|
||||||
],
|
|
||||||
测试分组114514: [
|
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "麻油衣酱",
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
userId: 0,
|
|
||||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
|
||||||
nickName: "Maya Fey",
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
const [navigationItemSelected, setNavigationItemSelected] = React.useState('Recents')
|
const [navigationItemSelected, setNavigationItemSelected] = React.useState('Recents')
|
||||||
@@ -125,15 +65,16 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// 最近聊天
|
// 最近聊天
|
||||||
(navigationItemSelected == "Recents") &&
|
|
||||||
<mdui-list style={{
|
<mdui-list style={{
|
||||||
width: "35%",
|
width: "35%",
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
paddingRight: '10px'
|
paddingRight: '10px',
|
||||||
|
display: navigationItemSelected == "Recents" ? null : 'none'
|
||||||
}}>
|
}}>
|
||||||
{
|
{
|
||||||
recentsList.map((v) =>
|
recentsList.map((v) =>
|
||||||
<RecentsListItem
|
<RecentsListItem
|
||||||
|
key={v.userId}
|
||||||
nickName={v.nickName}
|
nickName={v.nickName}
|
||||||
avatar={v.avatar}
|
avatar={v.avatar}
|
||||||
content={v.content} />
|
content={v.content} />
|
||||||
@@ -143,26 +84,29 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
// 联系人列表
|
// 联系人列表
|
||||||
(navigationItemSelected == "Contacts") &&
|
|
||||||
<mdui-list style={{
|
<mdui-list style={{
|
||||||
width: "35%",
|
width: "35%",
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
paddingRight: '10px'
|
paddingRight: '10px',
|
||||||
|
display: navigationItemSelected == "Contacts" ? null : 'none'
|
||||||
}}>
|
}}>
|
||||||
{
|
<mdui-collapse accordion value={Object.keys(contactsMap)[0]}>
|
||||||
Object.keys(contactsMap).map((v) =>
|
{
|
||||||
<>
|
Object.keys(contactsMap).map((v) =>
|
||||||
<mdui-list-subheader>{v}</mdui-list-subheader>
|
<mdui-collapse-item key={v} value={v}>
|
||||||
{
|
<mdui-list-subheader slot="header">{v}</mdui-list-subheader>
|
||||||
contactsMap[v].map((v2) =>
|
{
|
||||||
<ContactsListItem
|
contactsMap[v].map((v2) =>
|
||||||
nickName={v2.nickName}
|
<ContactsListItem
|
||||||
avatar={v2.avatar} />
|
key={v2.userId}
|
||||||
)
|
nickName={v2.nickName}
|
||||||
}
|
avatar={v2.avatar} />
|
||||||
</>
|
)
|
||||||
)
|
}
|
||||||
}
|
</mdui-collapse-item>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</mdui-collapse>
|
||||||
</mdui-list>
|
</mdui-list>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -192,55 +136,23 @@ export default function App() {
|
|||||||
<mdui-top-app-bar-title>Title</mdui-top-app-bar-title>
|
<mdui-top-app-bar-title>Title</mdui-top-app-bar-title>
|
||||||
<mdui-button-icon icon="more_vert"></mdui-button-icon>
|
<mdui-button-icon icon="more_vert"></mdui-button-icon>
|
||||||
</mdui-top-app-bar>
|
</mdui-top-app-bar>
|
||||||
<div>
|
<div style={{
|
||||||
<MessageContainer style={{
|
display: "flex",
|
||||||
height: '100%',
|
flexDirection: "column",
|
||||||
paddingBottom: '20px',
|
height: "100%",
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
}}>
|
}}>
|
||||||
|
<mdui-button variant="text">加載更多</mdui-button>
|
||||||
|
</div>
|
||||||
|
<MessageContainer>
|
||||||
<Message
|
<Message
|
||||||
nickName="Fey"
|
nickName="Fey"
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
||||||
Test
|
Test
|
||||||
</Message>
|
</Message>
|
||||||
<Message
|
|
||||||
direction="right"
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
<Message
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
<Message
|
|
||||||
direction="right"
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
<Message
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
<Message
|
|
||||||
direction="right"
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
<Message
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
<Message
|
|
||||||
direction="right"
|
|
||||||
nickName="Fey"
|
|
||||||
avatar="https://www.court-records.net/mugshot/aa6-004-maya.png">
|
|
||||||
Test
|
|
||||||
</Message>
|
|
||||||
</MessageContainer>
|
</MessageContainer>
|
||||||
{
|
{
|
||||||
// 输入框
|
// 输入框
|
||||||
@@ -265,17 +177,6 @@ export default function App() {
|
|||||||
marginRight: '7px',
|
marginRight: '7px',
|
||||||
}}></mdui-button-icon>
|
}}></mdui-button-icon>
|
||||||
</div>
|
</div>
|
||||||
{/* <mdui-top-app-bar style={{
|
|
||||||
position: 'sticky',
|
|
||||||
bottom: '0',
|
|
||||||
}}>
|
|
||||||
<mdui-button-icon icon="menu"></mdui-button-icon>
|
|
||||||
<mdui-top-app-bar-title>Title</mdui-top-app-bar-title>
|
|
||||||
<div style={{
|
|
||||||
flexGrow: 1,
|
|
||||||
}}></div>
|
|
||||||
<mdui-button-icon icon="more_vert"></mdui-button-icon>
|
|
||||||
</mdui-top-app-bar> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export default function MessageContainer({ children, style, ...props } = {}) {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
marginBottom: '20px',
|
||||||
|
height: "100%",
|
||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
{...props}>
|
{...props}>
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ async function compileJs(path: string) {
|
|||||||
console.log(`编译: ${path}`)
|
console.log(`编译: ${path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(source: string, output: string) {
|
export default async function(source: string, output: string) {
|
||||||
|
const t = Date.now()
|
||||||
io.copyDir(source, output)
|
io.copyDir(source, output)
|
||||||
io.listFiles(output, {
|
for (const v of io.listFiles(output, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
fullPath: true,
|
fullPath: true,
|
||||||
}).forEach(function (v) {
|
}))
|
||||||
if (/\.(t|j)sx?$/.test(v))
|
if (/\.(t|j)sx?$/.test(v))
|
||||||
compileJs(v)
|
await compileJs(v)
|
||||||
})
|
return (Date.now() - t) / 1000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ import HttpServerLike from "./types/HttpServerLike.ts"
|
|||||||
import config from './config.ts'
|
import config from './config.ts'
|
||||||
import http from 'node:http'
|
import http from 'node:http'
|
||||||
import https from 'node:https'
|
import https from 'node:https'
|
||||||
|
import readline from 'node:readline'
|
||||||
|
import process from "node:process"
|
||||||
import transform from './compiler/transform.ts'
|
import transform from './compiler/transform.ts'
|
||||||
|
import chalk from "chalk"
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
const url = req.originalUrl || req.url
|
const url = req.originalUrl || req.url
|
||||||
if (/\.(j|t)sx?/.test(url))
|
if (/\.(j|t)sx?$/.test(url))
|
||||||
res.setHeader('Content-Type', 'application/javascript')
|
res.setHeader('Content-Type', 'application/javascript')
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
@@ -31,5 +34,18 @@ ApiManager.initEvents()
|
|||||||
ApiManager.initAllApis()
|
ApiManager.initAllApis()
|
||||||
|
|
||||||
httpServer.listen(config.server.listen)
|
httpServer.listen(config.server.listen)
|
||||||
|
console.log(chalk.green("API & Web 服務已經開始運作"))
|
||||||
|
|
||||||
transform('./client', config.data_path + '/page_compiled')
|
console.log(chalk.green("Web 頁面已編譯完成, 用時 " + await transform('./client', config.data_path + '/page_compiled') + "s"))
|
||||||
|
|
||||||
|
console.log(chalk.yellow("===== TheWhiteSilk Server ====="))
|
||||||
|
console.log(chalk.yellow("b - 重新編譯 Web 頁面"))
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout
|
||||||
|
})
|
||||||
|
rl.on('line', async (text) => {
|
||||||
|
if (text == "b")
|
||||||
|
console.log(chalk.green("Web 頁面已編譯完成, 用時 " + await transform('./client', config.data_path + '/page_compiled') + "s"))
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user