Browse Source

change send images

master
godo 9 months ago
parent
commit
b55aac8fc8
  1. 1
      frontend/components.d.ts
  2. 25
      frontend/src/components/builtin/FileList.vue
  3. 2
      frontend/src/components/computer/Computer.vue
  4. 102
      frontend/src/components/localchat/ChatContent.vue
  5. 20
      frontend/src/components/localchat/ChatEditor.vue
  6. 12
      frontend/src/components/localchat/ChatFoot.vue
  7. 2
      frontend/src/stores/choose.ts
  8. 288
      frontend/src/stores/localchat.ts
  9. 2
      godo/cmd/main.go
  10. 23
      godo/localchat/addr.go
  11. 23
      godo/localchat/check.go
  12. 41
      godo/localchat/image.go
  13. 29
      godo/webdav/auth.go

1
frontend/components.d.ts

@ -56,6 +56,7 @@ declare module 'vue' {
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElLink: typeof import('element-plus/es')['ElLink']
ElOption: typeof import('element-plus/es')['ElOption']

25
frontend/src/components/builtin/FileList.vue

@ -77,7 +77,6 @@ import { throttle } from '@/util/debounce';
import { dealSize } from '@/util/file';
import { Menu } from '@/system/menu/Menu';
import { useChooseStore } from "@/stores/choose";
import { log } from 'console';
const { openPropsWindow, copyFile, createLink, deleteFile } = useContextMenu();
const sys = useSystem();
const { startDrag, folderDrop } = useFileDrag(sys);
@ -124,8 +123,16 @@ function getName(item: any) {
}
}
function handleOnOpen(item: OsFileWithoutContent) {
// props.onOpen(item);
// emitEvent('desktop.app.open');
chosenIndexs.value = [];
if (choose.ifShow && !item.isDirectory) {
choose.path.push(item.path)
choose.close()
} else {
props.onOpen(item);
emitEvent('desktop.app.open');
}
}
function hadnleDrop(mouse: DragEvent, path: string) {
hoverIndex.value = -1;
@ -170,10 +177,10 @@ function onEditNameEnd() {
newPath
);
props.onRefresh();
if(newPath.indexOf("Desktop") !== -1){
if (newPath.indexOf("Desktop") !== -1) {
sys.refershAppList()
}
}
editIndex.value = -1;
}
@ -320,8 +327,16 @@ function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index:
menuArr.push({
label: "选中发送",
click: () => {
choose.path = item.path
choose.close()
const paths: any = []
chosenIndexs.value.forEach((index) => {
const item = props.fileList[index];
paths.push(item.path)
})
if (paths.length > 0) {
choose.path = paths
choose.close()
}
chosenIndexs.value = [];
},
})
}

2
frontend/src/components/computer/Computer.vue

@ -248,7 +248,7 @@ async function onTreeOpen(path: string) {
if (file) {
openFolder(file);
}
console.log(path)
//console.log(path)
router_url.value = path;
}

102
frontend/src/components/localchat/ChatContent.vue

@ -6,7 +6,7 @@ import { formatChatTime } from "@/util/common";
// import "vditor/dist/index.css";
import { ElScrollbar } from "element-plus";
import { System } from "@/system";
const sys:any = inject<System>("system");
const sys: any = inject<System>("system");
const store = useLocalChatStore();
const messageContainerRef = ref<InstanceType<typeof ElScrollbar>>();
const messageInnerRef = ref<HTMLDivElement>();
@ -20,23 +20,23 @@ const scrollToBottom = () => {
// messageContainerRef.value!.setScrollTop(
// messageInnerRef.value!.clientHeight
// );
messageContainerRef.value.setScrollTop(messageInnerRef.value!.clientHeight);
messageContainerRef.value.setScrollTop(messageInnerRef.value!.clientHeight);
}
});
};
watch(
() => store.msgList,
(_) => {
if(!isScrool){
if (!isScrool) {
scrollToBottom();
}
},
{
deep: true,
}
);
function replaceIconTags(text:any) {
function replaceIconTags(text: any) {
// {**}
// console.log(text)
// text = Vditor.md2html(text);
@ -44,7 +44,7 @@ function replaceIconTags(text:any) {
const regex = /\{\-(.*?)\-\}/g;
// 使replace
const replacedText = text.replace(regex, (_:any, p1:string) => {
const replacedText = text.replace(regex, (_: any, p1: string) => {
// p1
return `<img src='/image/chat/emoji/${p1}.gif' style='width:30px;height:30px;' />`;
//return `![avatar](/image/chat/emoji/${p1}.gif)`
@ -64,29 +64,35 @@ async function scroll({ scrollTop }: { scrollTop: number }) {
<template>
<div class="chatContentContainer" v-if="store.chatTargetId > 0">
<div class="message-area">
<el-scrollbar
max-height="100%"
class="scrollbar-container"
@scroll="scroll"
ref="messageContainerRef">
<el-scrollbar max-height="100%" class="scrollbar-container" @scroll="scroll" ref="messageContainerRef">
<div ref="messageInnerRef" class="message-wrap">
<div v-for="(item, index) in store.msgList" :key="index" :class="['message-block', item.isMe ? 'mine' : 'theirs']">
<div v-for="(item, index) in store.msgList" :key="index"
:class="['message-block', item.isMe ? 'mine' : 'theirs']">
<div class="avatar-container">
<div class="icon-container">
<el-icon><component :is="item.isMe ? 'UserFilled' : 'Place'"/></el-icon>
<el-icon>
<component :is="item.isMe ? 'UserFilled' : 'Place'" />
</el-icon>
</div>
</div>
<div class="content">
<div
v-if="item.type === 'text'"
v-html="replaceIconTags(item.content)"
class="message-content">
<div v-if="item.type === 'text'" v-html="replaceIconTags(item.content)" class="message-content">
</div>
<div v-if="item.type === 'file'">
<div class="file-bubble">
<div class="file-content" v-for="el in item.content" @click="sys.openFile(el.path)">
<div class="file-icon"><FileIcon :file="el" /></div>
<div class="file-name">{{el.name}}</div>
<div class="file-icon">
<FileIcon :file="el" />
</div>
<div class="file-name">{{ el.name }}</div>
</div>
</div>
</div>
<div v-if="item.type === 'image'">
<div class="file-bubble">
<div class="message-content" v-for="el in item.content">
<el-image style="width: 100px; height: 100px" :src="el" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" :preview-src-list="item.content" :initial-index="4" fit="cover" />
</div>
</div>
</div>
@ -100,7 +106,7 @@ async function scroll({ scrollTop }: { scrollTop: number }) {
</div>
<div class="no-message-container" v-else>
<el-icon :size="180" color="#0078d7">
<ChatDotRound />
<ChatDotRound />
</el-icon>
</div>
</template>
@ -115,7 +121,7 @@ $win10-light-grey: #f2f2f2;
}
.message-area {
height: 420px;
}
@ -132,7 +138,7 @@ $win10-light-grey: #f2f2f2;
display: flex;
margin-bottom: 10px;
align-items: flex-end; //
flex-direction: row;
}
@ -178,61 +184,75 @@ $win10-light-grey: #f2f2f2;
.mine {
// 'mine'
justify-content: flex-end;
.content {
// 'mine'
// justify-content: flex-end; justify-content
align-items: flex-end; //
}
.message-content {
background-color: $win10-blue;
color: $win10-light-blue;
border-radius: 12px 2px 2px 2px; //
}
.avatar-container {
order: 1;
order: 1;
margin-left: 10px;
margin-top:-20px;
margin-top: -20px;
}
}
.theirs {
.content {
.content {
// 使
align-items: flex-end;
}
.message-content {
border-radius: 2px 12px 2px 2px;
}
}
.no-message-container {
height: 100%;
margin: 120px auto;
text-align: center;
justify-content: center;
height: 100%;
margin: 120px auto;
text-align: center;
justify-content: center;
}
.file-bubble {
background-color: #f0f0f0; /* 背景色,可以根据需要调整 */
border-radius: 10px; /* 圆角,让框看起来更柔和 */
padding: 10px; /* 内边距,给内容一些空间 */
margin-bottom: 10px; /* 气泡间的外边距,使它们看起来不紧凑 */
background-color: #f0f0f0;
/* 背景色,可以根据需要调整 */
border-radius: 10px;
/* 圆角,让框看起来更柔和 */
padding: 10px;
/* 内边距,给内容一些空间 */
margin-bottom: 10px;
/* 气泡间的外边距,使它们看起来不紧凑 */
max-width: 100%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 添加阴影效果,增强立体感 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
/* 添加阴影效果,增强立体感 */
}
.file-content {
display: flex;
align-items: center;
height:36px;
line-height:36px;
height: 36px;
line-height: 36px;
gap: 3px;
}
.file-content:hover {
background-color: #e0e0e0; /* 改变背景色,悬停时更浅或更深,根据设计调整 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 增强阴影效果,使气泡在悬停时更加突出 */
transition: all 0.3s ease; /* 添加过渡效果,使变化平滑 */
background-color: #e0e0e0;
/* 改变背景色,悬停时更浅或更深,根据设计调整 */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
/* 增强阴影效果,使气泡在悬停时更加突出 */
transition: all 0.3s ease;
/* 添加过渡效果,使变化平滑 */
}
.file-icon {
width: 18px;
height: 18px;

20
frontend/src/components/localchat/ChatEditor.vue

@ -12,7 +12,7 @@
@keydown.enter="keyDown($event)"
v-model="store.sendInfo" />
<el-tooltip placement="top" content="按enter键发送,按ctrl+enter键换行">
<el-icon :size="22" class="win11-chat-send-button" @click="store.sendMsg()">
<el-icon :size="22" class="win11-chat-send-button" @click="store.sendMsg('text')">
<Promotion />
</el-icon>
</el-tooltip>
@ -22,17 +22,18 @@
<script setup lang="ts">
import { useLocalChatStore } from "@/stores/localchat"
import { notifyError } from "@/util/msg";
//import { notifyError } from "@/util/msg";
const store = useLocalChatStore()
//
function keyDown(event: any) {
if(store.sendInfo == '')return
if (event.ctrlKey && event.keyCode === 13) {
store.sendInfo = store.sendInfo + "\n"
} else if (event.keyCode === 13) {
event.preventDefault() //
send()
store.sendMsg('text')
return false
}
}
@ -42,18 +43,13 @@ const handleDrop = (event:any) => {
const files = JSON.parse(frompathArrStr) as string[];
if (files && files.length > 0) {
//
console.log('Files dropped:', files);
store.uploadFile(files)
//console.log('Files dropped:', files);
store.sendInfo = files;
//store.uploadFile(files)
store.sendMsg('applyfile')
}
};
function send(){
// if(!store.hostInfo || !store.hostInfo.ip){
// notifyError("Please wait for a moment");
// return;
// }
store.sendMsg()
}
</script>

12
frontend/src/components/localchat/ChatFoot.vue

@ -64,16 +64,18 @@ const store = useLocalChatStore();
const choose = useChooseStore();
//const editor = ref(null)
const imgExt = ["png", "jpg", "jpeg", "gif", "bmp", "webp", "svg"];
const choosetype = ref('image')
//
function selectIcon(icon: string) {
store.sendInfo +=
"{-" + icon.replace("/image/chat/emoji/", "").replace(".gif", "") + "-}";
}
function selectImg() {
choosetype.value = 'image'
choose.select("选择图片", imgExt);
}
function selectFile() {
choosetype.value = 'applyfile'
choose.select("选择文件", "*");
}
watch(
@ -82,12 +84,10 @@ watch(
//console.log("userList :", newVal);
const paths = toRaw(newVal)
if(paths.length > 0){
store.uploadFile(paths).then(() => {
choose.path = []
})
store.sendInfo = paths;
choose.path = []
store.sendMsg(choosetype.value)
}
},
{ deep: true } // deep: true
);

2
frontend/src/stores/choose.ts

@ -3,7 +3,7 @@ import { BrowserWindow } from "@/system";
import { ref } from 'vue';
export const useChooseStore = defineStore('chooseStore', () => {
const win:any = ref()
const path:any = ref("")
const path:any = ref([])
const ifShow = ref(false)
const select = (title = '选择文件', fileExt:any) => {
win.value = new BrowserWindow({

288
frontend/src/stores/localchat.ts

@ -4,16 +4,15 @@ import { ref, toRaw, inject } from "vue";
import { db } from './db'
import { System } from "@/system";
import { getSystemConfig } from "@/system/config";
import { isBase64, base64ToBuffer } from "@/util/file";
import { isValidIP } from "@/util/common";
import { notifyError, notifySuccess } from "@/util/msg";
export const useLocalChatStore = defineStore('localChatStore', () => {
const config = getSystemConfig();
const sys = inject<System>("system");
//const sys = inject<System>("system");
const userList: any = ref([])
const msgList: any = ref([])
const contentList: any = ref([])
const OutUserList: any = ref([])
//const OutUserList: any = ref([])
const hostInfo: any = ref({})
const showChooseFile = ref(false)
const currentPage = ref(1)
@ -23,7 +22,7 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
{ index: 2, lable: "用户列表", icon: "UserFilled", type: "info" },
])
const navId = ref(1)
const sendInfo = ref("")
const sendInfo:any = ref()
const chatTargetId = ref(0)
const chatTargetIp = ref("")
const showAddUser = ref(false)
@ -49,6 +48,9 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
msg.message = msg.message.replaceAll("\\n", "\n")
//console.log(msg)
addText(msg)
}
if (msg.type === "image"){
}
if (msg.type === "fileSending"){
@ -192,8 +194,8 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
}
}
const getUserList = async () => {
const listAll = await db.getAll('chatuser')
const list = [...listAll, ...OutUserList.value]
const list = await db.getAll('chatuser')
//const list = [...listAll, ...OutUserList.value]
let uniqueIpMap = new Map<string, any>();
// 遍历 list 并添加 IP 地址到 Map 中
@ -338,168 +340,186 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
await updateContentList(saveMsg)
handleSelect(1)
}
const sendMsg = async () => {
const sendMsg = async (type:string) => {
if (chatTargetId.value < 1) {
return
}
const content = toRaw(sendInfo.value)
let saves:any
if (type === 'image') {
const apiUrl = `${config.apiUrl}/localchat/viewimage?img=`
saves = content.map((d: any) => `${apiUrl}${encodeURIComponent(d)}`)
}else{
saves = content
}
const saveMsg: any = {
type: 'text',
type: type,
targetId: chatTargetId.value,
targetIp: chatTargetIp.value,
content: sendInfo.value.trim(),
content: saves,
createdAt: Date.now(),
isMe: true,
isRead: false,
status: 'sending'
}
//console.log(saveMsg)
const msgId = await db.addOne('chatmsg', saveMsg)
//await getMsgList()
msgList.value.push(saveMsg)
const targetUser = userList.value.find((d: any) => d.ip === chatTargetIp.value)
//console.log(targetUser)
if (targetUser.isOnline) {
const postUrl = `${config.apiUrl}/localchat/message`
let postUrl = `${config.apiUrl}/localchat/message`
if(type === 'applyfile'){
postUrl = `${config.apiUrl}/localchat/applyfile`
}
if(type === 'image'){
postUrl = `${config.apiUrl}/localchat/sendimage`
}
const messages = {
type: 'text',
message: saveMsg.content,
type: type,
message: content,
ip: saveMsg.targetIp
}
const completion = await fetch(postUrl, {
method: "POST",
body: JSON.stringify(messages),
})
//console.log(completion)
if (!completion.ok) {
console.log(completion)
notifyError("发送失败!")
} else {
saveMsg.isRead = true
saveMsg.status = 'sended'
saveMsg.readAt = Date.now()
await db.update('chatmsg', msgId, saveMsg)
if(type === 'applyfile'){
notifySuccess("发送成功!")
}
}
}
sendInfo.value = ""
await updateContentList(saveMsg)
}
//上传文件资源
async function uploadFile(paths: any) {
if (chatTargetId.value < 1) {
return
}
const targetUser = userList.value.find((d: any) => d.ip === chatTargetIp.value)
if (!targetUser.isOnline) {
notifyError("The user is not online!");
return;
}
if (!hostInfo.value || !hostInfo.value.ip) {
notifyError("Please wait for a moment");
return;
}
//console.log(paths)
const formData = new FormData();
const errstr: any = []
const files: any = []
for (let i = 0; i < paths.length; i++) {
const content = await sys?.fs.readFile(paths[i]);
let blobContent;
if (!content || content == '') {
errstr.push(paths[i] + " is empty")
continue
}
if (content instanceof ArrayBuffer) {
blobContent = new Blob([content]);
}
else if (typeof content === 'string') {
if (isBase64(content)) {
const base64 = base64ToBuffer(content);
blobContent = new Blob([base64]);
} else {
blobContent = new Blob([content], { type: "text/plain;charset=utf-8" });
}
}
else {
errstr.push(paths[i] + " type is error")
continue
}
const fileName = paths[i].split("/").pop()
files.push({
name: fileName,
path: paths[i],
ext: fileName.split(".").pop(),
})
//files.push(blobContent);
formData.append(`files`, blobContent, fileName);
}
if (errstr.length > 0) {
errstr.forEach((d: any) => {
notifyError(d);
})
return
}
//formData.append("files", files);
formData.append("ip", hostInfo.value.ip);
formData.append("hostname", hostInfo.value.hostname);
//console.log(formData)
const postUrl = `http://${targetUser.ip}:56780/localchat/upload`
const res = await fetch(postUrl, {
method: "POST",
body: formData,
});
if (!res.ok) {
console.log(res);
notifyError("Upload error!");
return;
}
const saveMsg: any = {
type: 'file',
targetId: targetUser.id,
targetIp: targetUser.ip,
content: files,
reciperInfo: toRaw(targetUser),
createdAt: Date.now(),
isMe: false,
isRead: true,
status: 'reciped'
}
//console.log(saveMsg)
await db.addOne('chatmsg', saveMsg)
msgList.value.push(saveMsg)
// async function uploadFile(paths: any) {
// if (chatTargetId.value < 1) {
// return
// }
// const targetUser = userList.value.find((d: any) => d.ip === chatTargetIp.value)
// if (!targetUser.isOnline) {
// notifyError("The user is not online!");
// return;
// }
// if (!hostInfo.value || !hostInfo.value.ip) {
// notifyError("Please wait for a moment");
// return;
// }
// //console.log(paths)
// const formData = new FormData();
// const errstr: any = []
// const files: any = []
// for (let i = 0; i < paths.length; i++) {
// const content = await sys?.fs.readFile(paths[i]);
// let blobContent;
// if (!content || content == '') {
// errstr.push(paths[i] + " is empty")
// continue
// }
// if (content instanceof ArrayBuffer) {
// blobContent = new Blob([content]);
// }
// else if (typeof content === 'string') {
// if (isBase64(content)) {
// const base64 = base64ToBuffer(content);
// blobContent = new Blob([base64]);
// } else {
// blobContent = new Blob([content], { type: "text/plain;charset=utf-8" });
// }
// }
// else {
// errstr.push(paths[i] + " type is error")
// continue
// }
// const fileName = paths[i].split("/").pop()
// files.push({
// name: fileName,
// path: paths[i],
// ext: fileName.split(".").pop(),
// })
// //files.push(blobContent);
// formData.append(`files`, blobContent, fileName);
// }
// if (errstr.length > 0) {
// errstr.forEach((d: any) => {
// notifyError(d);
// })
// return
// }
// //formData.append("files", files);
// formData.append("ip", hostInfo.value.ip);
// formData.append("hostname", hostInfo.value.hostname);
// //console.log(formData)
// const postUrl = `http://${targetUser.ip}:56780/localchat/upload`
// const res = await fetch(postUrl, {
// method: "POST",
// body: formData,
// });
// if (!res.ok) {
// console.log(res);
// notifyError("Upload error!");
// return;
// }
// const saveMsg: any = {
// type: 'file',
// targetId: targetUser.id,
// targetIp: targetUser.ip,
// content: files,
// reciperInfo: toRaw(targetUser),
// createdAt: Date.now(),
// isMe: false,
// isRead: true,
// status: 'reciped'
// }
// //console.log(saveMsg)
// await db.addOne('chatmsg', saveMsg)
// msgList.value.push(saveMsg)
notifySuccess("upload success!");
}
// notifySuccess("upload success!");
// }
async function addUser(ip: string) {
if (!isValidIP(ip)) {
notifyError("请输入正确的IP地址");
return
}
const postUrl = `http://${ip}:56780/localchat/check`
const completion = await fetch(postUrl)
if (!completion.ok) {
notifyError('用户不在线')
} else {
const res = await completion.json()
const data = res.data
data.createdAt = Date.now()
data.updatedAt = Date.now()
data.isOnline = true
data.username = data.hostname
if(ip != data.ip){
notifyError(`IP地址不一致,可能会存在不通的问题`)
}
data.ip = ip
if (!OutUserList.value.some((item: any) => item.ip === data.ip)) {
OutUserList.value.push(data);
await getUserList()
}
showAddUser.value = false
//async function addUser(ip: string) {
// if (!isValidIP(ip)) {
// notifyError("请输入正确的IP地址");
// return
// }
// const postUrl = `http://${ip}:56780/localchat/check`
// const completion = await fetch(postUrl)
// if (!completion.ok) {
// notifyError('用户不在线')
// } else {
// const res = await completion.json()
// const data = res.data
// data.createdAt = Date.now()
// data.updatedAt = Date.now()
// data.isOnline = true
// data.username = data.hostname
// if(ip != data.ip){
// notifyError(`IP地址不一致,可能会存在不通的问题`)
// }
// data.ip = ip
// if (!OutUserList.value.some((item: any) => item.ip === data.ip)) {
// OutUserList.value.push(data);
// await getUserList()
// }
// showAddUser.value = false
}
// }
}
//}
return {
userList,
navList,
@ -514,7 +534,7 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
showChooseFile,
pageSize,
showAddUser,
OutUserList,
//OutUserList,
init,
setUserList,
getUserList,
@ -523,23 +543,11 @@ export const useLocalChatStore = defineStore('localChatStore', () => {
sendMsg,
addText,
addFile,
uploadFile,
//uploadFile,
moreMsgList,
refreshUserList,
clearMsg,
addUser,
//addUser,
handlerMessage
}
}, {
persist: {
enabled: true,
strategies: [
{
storage: localStorage,
paths: [
"OutUserList"
]
}, // name 字段用localstorage存储
],
}
})

2
godo/cmd/main.go

@ -106,7 +106,7 @@ func OsStart() {
localchatRouter.HandleFunc("/applyfile", localchat.HandlerApplySendFile).Methods(http.MethodPost)
localchatRouter.HandleFunc("/accessfile", localchat.HandlerAccessFile).Methods(http.MethodPost)
localchatRouter.HandleFunc("/sendimage", localchat.HandlerSendImg).Methods(http.MethodPost)
localchatRouter.HandleFunc("/viewimage", localchat.HandleViewImg).Methods(http.MethodPost)
localchatRouter.HandleFunc("/viewimage", localchat.HandleViewImg).Methods(http.MethodGet)
localchatRouter.HandleFunc("/setting", localchat.HandleAddr).Methods(http.MethodPost)
localchatRouter.HandleFunc("/getsetting", localchat.HandleGetAddr).Methods(http.MethodGet)
// 注册 WebDAV 路由

23
godo/localchat/addr.go

@ -22,29 +22,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// MIT License
//
// Copyright (c) 2024 godoos.com
// Email: xpbb@qq.com
// GitHub: github.com/phpk/godoos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package localchat
import (

23
godo/localchat/check.go

@ -22,29 +22,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// MIT License
//
// Copyright (c) 2024 godoos.com
// Email: xpbb@qq.com
// GitHub: github.com/phpk/godoos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package localchat
import (

41
godo/localchat/image.go

@ -25,15 +25,14 @@
package localchat
import (
"bytes"
"encoding/json"
"fmt"
"godo/libs"
"image"
"io"
"log"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"time"
@ -61,21 +60,25 @@ func HandlerSendImg(w http.ResponseWriter, r *http.Request) {
log.Printf("GetOsDir error: %v", err)
return
}
paths, ok := msg.Message.([]string)
log.Printf("send image to %v", msg.Message)
log.Printf("Type of msg.Message: %T", msg.Message)
paths, ok := msg.Message.([]interface{})
log.Printf("paths: %v", paths)
if !ok {
log.Printf("invalid message type")
libs.ErrorMsg(w, "HandleMessage message error")
return
}
for _, p := range paths {
for _, v := range paths {
p, ok := v.(string)
if !ok {
continue
}
filePath := filepath.Join(basePath, p)
// 处理多张图片
if fileInfo, err := os.Stat(filePath); err == nil {
if !fileInfo.IsDir() {
if isImage(filePath) { // 检查是否为图片
sendImage(filePath, toIp, msg)
} else {
log.Printf("文件 %s 不是图片", filePath)
}
sendImage(filePath, toIp, msg)
}
} else {
continue
@ -83,14 +86,7 @@ func HandlerSendImg(w http.ResponseWriter, r *http.Request) {
}
libs.SuccessMsg(w, nil, "图片发送成功")
}
func isImage(filePath string) bool {
data, err := os.ReadFile(filePath)
if err != nil {
return false
}
img, _, err := image.DecodeConfig(bytes.NewReader(data))
return err == nil && img.Width > 0 && img.Height > 0
}
func sendImage(filePath string, toIp string, message UdpMessage) {
// 打开文件
file, err := os.Open(filePath)
@ -212,15 +208,20 @@ func HandleViewImg(w http.ResponseWriter, r *http.Request) {
libs.ErrorMsg(w, "img is empty")
return
}
decodedImgParam, err := url.QueryUnescape(img)
if err != nil {
log.Fatalf("Error unescaping image parameter: %v", err)
}
basePath, err := libs.GetOsDir()
if err != nil {
log.Printf("GetOsDir error: %v", err)
return
}
filePath := filepath.Join(basePath, img)
filePath := filepath.Join(basePath, decodedImgParam)
log.Printf("filePath: %s", filePath)
// 检查文件是否存在
if _, err := os.Stat(filePath); os.IsNotExist(err) {
http.NotFound(w, r)
libs.ErrorMsg(w, "file not found")
return
}

29
godo/webdav/auth.go

@ -47,35 +47,6 @@ type Authorizer interface {
AddAuthenticator(key string, fn AuthFactory)
}
// An Authenticator implements a specific way to authorize requests.
// Each request is bound to a separate Authenticator instance.
//
// The authentication flow itself is broken down into `Authorize`
// and `Verify` steps. The former method runs before, and the latter
// runs after the `Request` is submitted.
// This makes it easy to encapsulate and control complex
// authentication challenges.
//
// Some authentication flows causing authentication round trips,
// which can be archived by returning the `redo` of the Verify
// method. `True` restarts the authentication process for the
// current action: A new `Request` is spawned, which must be
// authorized, sent, and re-verified again, until the action
// is successfully submitted.
// The preferred way is to handle the authentication ping-pong
// within `Verify`, and then `redo` with fresh credentials.
//
// The result of the `Verify` method can also trigger an
// `Authenticator` change by returning the `ErrAuthChanged`
// as an error. Depending on the `Authorizer` this may trigger
// an `Authenticator` negotiation.
//
// Set the `XInhibitRedirect` header to '1' in the `Authorize`
// method to get control over request redirection.
// Attention! You must handle the incoming request yourself.
//
// To store a shared session state the `Clone` method **must**
// return a new instance, initialized with the shared state.
type Authenticator interface {
// Authorizes a request. Usually by adding some authorization headers.
Authorize(c *http.Client, rq *http.Request, path string) error

Loading…
Cancel
Save