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/
|
||||
|
||||
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",
|
||||
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({
|
||||
默认分组: [
|
||||
所有: [
|
||||
{
|
||||
userId: 0,
|
||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
||||
@@ -78,19 +30,7 @@ export default function App() {
|
||||
userId: 0,
|
||||
avatar: "https://www.court-records.net/mugshot/aa6-004-maya.png",
|
||||
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')
|
||||
@@ -125,15 +65,16 @@ export default function App() {
|
||||
}
|
||||
{
|
||||
// 最近聊天
|
||||
(navigationItemSelected == "Recents") &&
|
||||
<mdui-list style={{
|
||||
width: "35%",
|
||||
overflowY: 'auto',
|
||||
paddingRight: '10px'
|
||||
paddingRight: '10px',
|
||||
display: navigationItemSelected == "Recents" ? null : 'none'
|
||||
}}>
|
||||
{
|
||||
recentsList.map((v) =>
|
||||
<RecentsListItem
|
||||
key={v.userId}
|
||||
nickName={v.nickName}
|
||||
avatar={v.avatar}
|
||||
content={v.content} />
|
||||
@@ -143,26 +84,29 @@ export default function App() {
|
||||
}
|
||||
{
|
||||
// 联系人列表
|
||||
(navigationItemSelected == "Contacts") &&
|
||||
<mdui-list style={{
|
||||
width: "35%",
|
||||
overflowY: 'auto',
|
||||
paddingRight: '10px'
|
||||
paddingRight: '10px',
|
||||
display: navigationItemSelected == "Contacts" ? null : 'none'
|
||||
}}>
|
||||
{
|
||||
Object.keys(contactsMap).map((v) =>
|
||||
<>
|
||||
<mdui-list-subheader>{v}</mdui-list-subheader>
|
||||
{
|
||||
contactsMap[v].map((v2) =>
|
||||
<ContactsListItem
|
||||
nickName={v2.nickName}
|
||||
avatar={v2.avatar} />
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
<mdui-collapse accordion value={Object.keys(contactsMap)[0]}>
|
||||
{
|
||||
Object.keys(contactsMap).map((v) =>
|
||||
<mdui-collapse-item key={v} value={v}>
|
||||
<mdui-list-subheader slot="header">{v}</mdui-list-subheader>
|
||||
{
|
||||
contactsMap[v].map((v2) =>
|
||||
<ContactsListItem
|
||||
key={v2.userId}
|
||||
nickName={v2.nickName}
|
||||
avatar={v2.avatar} />
|
||||
)
|
||||
}
|
||||
</mdui-collapse-item>
|
||||
)
|
||||
}
|
||||
</mdui-collapse>
|
||||
</mdui-list>
|
||||
}
|
||||
{
|
||||
@@ -192,55 +136,23 @@ export default function App() {
|
||||
<mdui-top-app-bar-title>Title</mdui-top-app-bar-title>
|
||||
<mdui-button-icon icon="more_vert"></mdui-button-icon>
|
||||
</mdui-top-app-bar>
|
||||
<div>
|
||||
<MessageContainer style={{
|
||||
height: '100%',
|
||||
paddingBottom: '20px',
|
||||
<div style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
}}>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}>
|
||||
<mdui-button variant="text">加載更多</mdui-button>
|
||||
</div>
|
||||
<MessageContainer>
|
||||
<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>
|
||||
<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>
|
||||
{
|
||||
// 输入框
|
||||
@@ -265,17 +177,6 @@ export default function App() {
|
||||
marginRight: '7px',
|
||||
}}></mdui-button-icon>
|
||||
</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>
|
||||
|
||||
@@ -9,6 +9,8 @@ export default function MessageContainer({ children, style, ...props } = {}) {
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
marginBottom: '20px',
|
||||
height: "100%",
|
||||
...style,
|
||||
}}
|
||||
{...props}>
|
||||
|
||||
@@ -24,13 +24,14 @@ async function compileJs(path: string) {
|
||||
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.listFiles(output, {
|
||||
for (const v of io.listFiles(output, {
|
||||
recursive: true,
|
||||
fullPath: true,
|
||||
}).forEach(function (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 http from 'node:http'
|
||||
import https from 'node:https'
|
||||
import readline from 'node:readline'
|
||||
import process from "node:process"
|
||||
import transform from './compiler/transform.ts'
|
||||
import chalk from "chalk"
|
||||
|
||||
const app = express()
|
||||
app.use((req, res, next) => {
|
||||
const url = req.originalUrl || req.url
|
||||
if (/\.(j|t)sx?/.test(url))
|
||||
if (/\.(j|t)sx?$/.test(url))
|
||||
res.setHeader('Content-Type', 'application/javascript')
|
||||
next()
|
||||
})
|
||||
@@ -31,5 +34,18 @@ ApiManager.initEvents()
|
||||
ApiManager.initAllApis()
|
||||
|
||||
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