listing of files on connect
This commit is contained in:
parent
1505abb703
commit
70e84c5840
@ -8,7 +8,8 @@
|
|||||||
<div v-for="item in files" :key="item.name" class="files-list-item">
|
<div v-for="item in files" :key="item.name" class="files-list-item">
|
||||||
<i :class="fileIcon(item)" />
|
<i :class="fileIcon(item)" />
|
||||||
<p>{{ item.name }}</p>
|
<p>{{ item.name }}</p>
|
||||||
<i class="fas fa-download download" @click="() => download(item)" />
|
<i v-if="item.type !== 'dir'" class="fas fa-download download"
|
||||||
|
@click="() => download(item)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="files-transfer" @dragover.prevent @drop.prevent="onFileDrop">
|
<div class="files-transfer" @dragover.prevent @drop.prevent="onFileDrop">
|
||||||
@ -111,7 +112,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import { Component, Vue } from 'vue-property-decorator'
|
import { Component, Vue } from 'vue-property-decorator'
|
||||||
import { onMounted, onUnmounted } from 'vue'
|
|
||||||
|
|
||||||
import Markdown from './markdown'
|
import Markdown from './markdown'
|
||||||
import Content from './context.vue'
|
import Content from './context.vue'
|
||||||
@ -125,28 +125,12 @@
|
|||||||
})
|
})
|
||||||
export default class extends Vue {
|
export default class extends Vue {
|
||||||
|
|
||||||
data() {
|
get cwd() {
|
||||||
return {
|
return this.$accessor.files.cwd
|
||||||
cwd: '~/Downloads',
|
}
|
||||||
files: [
|
|
||||||
{
|
get files() {
|
||||||
name: 'a.txt',
|
return this.$accessor.files.files
|
||||||
type: 'file'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'b',
|
|
||||||
type: 'dir'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'c.mkv',
|
|
||||||
type: 'file'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'd.mp3',
|
|
||||||
type: 'file'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
|
@ -38,6 +38,13 @@ export const EVENT = {
|
|||||||
MESSAGE: 'chat/message',
|
MESSAGE: 'chat/message',
|
||||||
EMOTE: 'chat/emote',
|
EMOTE: 'chat/emote',
|
||||||
},
|
},
|
||||||
|
FILETRANSFER: {
|
||||||
|
ENABLE: 'filetransfer/enable',
|
||||||
|
DISABLE: 'filetransfer/disable',
|
||||||
|
UNPRIVENABLE: 'filetransfer/unprivenable',
|
||||||
|
UNPRIVDISABLE: 'filetransfer/unprivdisable',
|
||||||
|
LIST: 'filetransfer/list'
|
||||||
|
},
|
||||||
SCREEN: {
|
SCREEN: {
|
||||||
CONFIGURATIONS: 'screen/configurations',
|
CONFIGURATIONS: 'screen/configurations',
|
||||||
RESOLUTION: 'screen/resolution',
|
RESOLUTION: 'screen/resolution',
|
||||||
@ -69,6 +76,7 @@ export type WebSocketEvents =
|
|||||||
| MemberEvents
|
| MemberEvents
|
||||||
| SignalEvents
|
| SignalEvents
|
||||||
| ChatEvents
|
| ChatEvents
|
||||||
|
| FileTransferEvents
|
||||||
| ScreenEvents
|
| ScreenEvents
|
||||||
| BroadcastEvents
|
| BroadcastEvents
|
||||||
| AdminEvents
|
| AdminEvents
|
||||||
@ -91,6 +99,14 @@ export type SignalEvents =
|
|||||||
| typeof EVENT.SIGNAL.CANDIDATE
|
| typeof EVENT.SIGNAL.CANDIDATE
|
||||||
|
|
||||||
export type ChatEvents = typeof EVENT.CHAT.MESSAGE | typeof EVENT.CHAT.EMOTE
|
export type ChatEvents = typeof EVENT.CHAT.MESSAGE | typeof EVENT.CHAT.EMOTE
|
||||||
|
|
||||||
|
export type FileTransferEvents =
|
||||||
|
| typeof EVENT.FILETRANSFER.ENABLE
|
||||||
|
| typeof EVENT.FILETRANSFER.DISABLE
|
||||||
|
| typeof EVENT.FILETRANSFER.UNPRIVENABLE
|
||||||
|
| typeof EVENT.FILETRANSFER.UNPRIVDISABLE
|
||||||
|
| typeof EVENT.FILETRANSFER.LIST
|
||||||
|
|
||||||
export type ScreenEvents = typeof EVENT.SCREEN.CONFIGURATIONS | typeof EVENT.SCREEN.RESOLUTION | typeof EVENT.SCREEN.SET
|
export type ScreenEvents = typeof EVENT.SCREEN.CONFIGURATIONS | typeof EVENT.SCREEN.RESOLUTION | typeof EVENT.SCREEN.SET
|
||||||
|
|
||||||
export type BroadcastEvents =
|
export type BroadcastEvents =
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
AdminLockMessage,
|
AdminLockMessage,
|
||||||
SystemInitPayload,
|
SystemInitPayload,
|
||||||
AdminLockResource,
|
AdminLockResource,
|
||||||
|
FileTransferListPayload,
|
||||||
} from './messages'
|
} from './messages'
|
||||||
|
|
||||||
interface NekoEvents extends BaseEvents {}
|
interface NekoEvents extends BaseEvents {}
|
||||||
@ -351,6 +352,14 @@ export class NekoClient extends BaseClient implements EventEmitter<NekoEvents> {
|
|||||||
this.$accessor.chat.newEmote({ type: emote })
|
this.$accessor.chat.newEmote({ type: emote })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Chat Events
|
||||||
|
/////////////////////////////
|
||||||
|
protected [EVENT.FILETRANSFER.LIST]({ cwd, files }: FileTransferListPayload) {
|
||||||
|
this.$accessor.files.setCwd(cwd)
|
||||||
|
this.$accessor.files.setFileList(files)
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Screen Events
|
// Screen Events
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
|
@ -8,8 +8,14 @@ import {
|
|||||||
ChatEvents,
|
ChatEvents,
|
||||||
ScreenEvents,
|
ScreenEvents,
|
||||||
AdminEvents,
|
AdminEvents,
|
||||||
|
FileTransferEvents,
|
||||||
} from './events'
|
} from './events'
|
||||||
import { Member, ScreenConfigurations, ScreenResolution } from './types'
|
import {
|
||||||
|
FileListItem,
|
||||||
|
Member,
|
||||||
|
ScreenConfigurations,
|
||||||
|
ScreenResolution
|
||||||
|
} from './types'
|
||||||
|
|
||||||
export type WebSocketMessages =
|
export type WebSocketMessages =
|
||||||
| WebSocketMessage
|
| WebSocketMessage
|
||||||
@ -192,6 +198,15 @@ export interface EmojiSendPayload {
|
|||||||
emote: string
|
emote: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// file transfer
|
||||||
|
export interface FileTransferMessage extends WebSocketMessage, FileTransferListPayload {
|
||||||
|
event: FileTransferEvents
|
||||||
|
}
|
||||||
|
export interface FileTransferListPayload {
|
||||||
|
cwd: string,
|
||||||
|
files: FileListItem[]
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SCREEN PAYLOADS
|
SCREEN PAYLOADS
|
||||||
*/
|
*/
|
||||||
|
@ -22,3 +22,8 @@ export interface ScreenResolution {
|
|||||||
height: number
|
height: number
|
||||||
rate: number
|
rate: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FileListItem {
|
||||||
|
name: string,
|
||||||
|
type: 'file' | 'dir'
|
||||||
|
}
|
||||||
|
35
client/src/store/files.ts
Normal file
35
client/src/store/files.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { actionTree, getterTree, mutationTree } from 'typed-vuex'
|
||||||
|
import { FileListItem } from '~/neko/types'
|
||||||
|
import { accessor } from '~/store'
|
||||||
|
|
||||||
|
export const state = () => ({
|
||||||
|
cwd: '',
|
||||||
|
files: [] as FileListItem[]
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getters = getterTree(state, {
|
||||||
|
//
|
||||||
|
})
|
||||||
|
|
||||||
|
export const mutations = mutationTree(state, {
|
||||||
|
_setCwd(state, cwd: string) {
|
||||||
|
state.cwd = cwd
|
||||||
|
},
|
||||||
|
|
||||||
|
_setFileList(state, files: FileListItem[]) {
|
||||||
|
state.files = files
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const actions = actionTree(
|
||||||
|
{ state, getters, mutations },
|
||||||
|
{
|
||||||
|
setCwd(store, cwd: string) {
|
||||||
|
accessor.files._setCwd(cwd)
|
||||||
|
},
|
||||||
|
|
||||||
|
setFileList(store, files: FileListItem[]) {
|
||||||
|
accessor.files._setFileList(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
@ -7,6 +7,7 @@ import { get, set } from '~/utils/localstorage'
|
|||||||
|
|
||||||
import * as video from './video'
|
import * as video from './video'
|
||||||
import * as chat from './chat'
|
import * as chat from './chat'
|
||||||
|
import * as files from './files'
|
||||||
import * as remote from './remote'
|
import * as remote from './remote'
|
||||||
import * as user from './user'
|
import * as user from './user'
|
||||||
import * as settings from './settings'
|
import * as settings from './settings'
|
||||||
@ -97,7 +98,7 @@ export const storePattern = {
|
|||||||
state,
|
state,
|
||||||
mutations,
|
mutations,
|
||||||
actions,
|
actions,
|
||||||
modules: { video, chat, user, remote, settings, client, emoji },
|
modules: { video, chat, files, user, remote, settings, client, emoji },
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
@ -34,6 +34,14 @@ const (
|
|||||||
CHAT_EMOTE = "chat/emote"
|
CHAT_EMOTE = "chat/emote"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FILETRANSFER_ENABLE = "filetransfer/enable"
|
||||||
|
FILETRANSFER_DISABLE = "filetransfer/disable"
|
||||||
|
FILETRANSFER_UNPRIVENABLE = "filetransfer/unprivenable"
|
||||||
|
FILETRANSFER_UNPRIVDISABLE = "filetransfer/unprivdisable"
|
||||||
|
FILETRANSFER_LIST = "filetransfer/list"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SCREEN_CONFIGURATIONS = "screen/configurations"
|
SCREEN_CONFIGURATIONS = "screen/configurations"
|
||||||
SCREEN_RESOLUTION = "screen/resolution"
|
SCREEN_RESOLUTION = "screen/resolution"
|
||||||
|
@ -106,6 +106,17 @@ type EmoteSend struct {
|
|||||||
Emote string `json:"emote"`
|
Emote string `json:"emote"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileTransferTarget struct {
|
||||||
|
Event string `json:"event"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileList struct {
|
||||||
|
Event string `json:"event"`
|
||||||
|
Cwd string `json:"cwd"`
|
||||||
|
Files []types.FileListItem `json:"files"`
|
||||||
|
}
|
||||||
|
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
@ -37,3 +37,8 @@ type WebSocketHandler interface {
|
|||||||
CanTransferFiles(password string) (bool, error)
|
CanTransferFiles(password string) (bool, error)
|
||||||
MakeFilePath(filename string) string
|
MakeFilePath(filename string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileListItem struct {
|
||||||
|
Filename string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
30
server/internal/utils/files.go
Normal file
30
server/internal/utils/files.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"m1k1o/neko/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListFiles(path string) (*[]types.FileListItem, error) {
|
||||||
|
items, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]types.FileListItem, len(items))
|
||||||
|
for i, item := range items {
|
||||||
|
var itemType string = ""
|
||||||
|
if item.IsDir() {
|
||||||
|
itemType = "dir"
|
||||||
|
} else {
|
||||||
|
itemType = "file"
|
||||||
|
}
|
||||||
|
out[i] = types.FileListItem{
|
||||||
|
Filename: item.Name(),
|
||||||
|
Type: itemType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &out, nil
|
||||||
|
}
|
@ -133,6 +133,21 @@ func (ws *WebSocketHandler) Start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send file list if necessary
|
||||||
|
if session.Admin() && ws.conf.FileTransfer || ws.conf.FileTransfer && ws.conf.UnprivFileTransfer {
|
||||||
|
files, err := utils.ListFiles(ws.conf.FileTransferPath)
|
||||||
|
if err == nil {
|
||||||
|
if err := session.Send(
|
||||||
|
message.FileList{
|
||||||
|
Event: event.FILETRANSFER_LIST,
|
||||||
|
Cwd: ws.conf.FileTransferPath,
|
||||||
|
Files: *files,
|
||||||
|
}); err != nil {
|
||||||
|
ws.logger.Warn().Err(err).Msg("file list event has failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove outdated stats
|
// remove outdated stats
|
||||||
if session.Admin() {
|
if session.Admin() {
|
||||||
ws.lastAdminLeftAt = nil
|
ws.lastAdminLeftAt = nil
|
||||||
|
Reference in New Issue
Block a user