-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathcoordinator.ts
More file actions
133 lines (120 loc) · 5.35 KB
/
coordinator.ts
File metadata and controls
133 lines (120 loc) · 5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import {
deleteDir, judgeDirExist, makeDir, readFile, writeFile,
type WebDAVAuth, type WebDAVContext,
} from "@api/web-dav"
import DateIterator from "@util/date-iterator"
import { processDir } from "../common"
import { CLIENT_FILE_NAME, convertClients2Markdown, divideByDate, parseData } from "../markdown"
function getEndpoint(ext: timer.backup.TypeExt | undefined): string | undefined {
let { endpoint } = ext || {}
if (endpoint?.endsWith('/')) {
endpoint = endpoint.substring(0, endpoint.length - 1)
}
return endpoint
}
function prepareContext(context: timer.backup.CoordinatorContext<never>): WebDAVContext {
const { auth, ext } = context
const endpoint = getEndpoint(ext)
if (!endpoint) {
throw new Error('Endpoint must be empty. This can\'t happen, please contact the developer')
}
const { acc: username, psw: password } = auth?.login || {}
if (!username || !password) {
throw new Error('Neither username nor password must be empty. This can\'t happen, please contact the developer')
}
const webDavAuth: WebDAVAuth = { type: "password", username, password }
return { auth: webDavAuth, endpoint }
}
export default class WebDAVCoordinator implements timer.backup.Coordinator<never> {
async updateClients(context: timer.backup.CoordinatorContext<never>, clients: timer.backup.Client[]): Promise<void> {
const dirPath = processDir(context?.ext?.dirPath)
const clientFilePath = `${dirPath}${CLIENT_FILE_NAME}`
const content = convertClients2Markdown(clients)
const davContext = prepareContext(context)
await writeFile(davContext, clientFilePath, content)
}
async listAllClients(context: timer.backup.CoordinatorContext<never>): Promise<timer.backup.Client[]> {
const dirPath = processDir(context?.ext?.dirPath)
const clientFilePath = `${dirPath}${CLIENT_FILE_NAME}`
const davContext = prepareContext(context)
try {
const content = await readFile(davContext, clientFilePath)
return parseData(content) ?? []
} catch (e) {
console.warn("Failed to read WebDav file content", e)
return []
}
}
async download(context: timer.backup.CoordinatorContext<never>, dateStart: Date, dateEnd: Date, targetCid?: string): Promise<timer.core.Row[]> {
const dirPath = processDir(context?.ext?.dirPath)
const davContext = prepareContext(context)
targetCid = targetCid || context?.cid
const dateIterator = new DateIterator(dateStart, dateEnd)
const result: timer.core.Row[] = []
await Promise.all(dateIterator.toArray().map(async date => {
const filePath = `${dirPath}${targetCid}/${date}.md`
const fileContent = await readFile(davContext, filePath)
const rows = parseData<timer.core.Row[]>(fileContent)
rows?.forEach?.(row => result.push(row))
}))
return result
}
async upload(context: timer.backup.CoordinatorContext<never>, rows: timer.core.Row[]): Promise<void> {
const dateAndContents = divideByDate(rows)
const dirPath = processDir(context?.ext?.dirPath)
const cid = context?.cid
const davContext = prepareContext(context)
const clientPath = await this.checkClientDirExist(davContext, dirPath, cid)
await Promise.all(
Object.entries(dateAndContents).map(async ([date, content]) => {
const filePath = `${clientPath}/${date}.md`
await writeFile(davContext, filePath, content)
})
)
}
private async checkClientDirExist(davContext: WebDAVContext, dirPath: string, cid: string) {
const clientDirPath = `${dirPath}${cid}`
const clientExist = await judgeDirExist(davContext, clientDirPath)
if (!clientExist) {
await makeDir(davContext, clientDirPath)
}
return clientDirPath
}
async testAuth(auth: timer.backup.Auth, ext: timer.backup.TypeExt): Promise<string | undefined> {
const endpoint = getEndpoint(ext)
if (!endpoint) {
return "The endpoint is blank"
}
const { dirPath } = ext || {}
if (!dirPath) {
return "The path of directory is blank"
}
const { acc, psw } = auth?.login || {}
if (!acc) {
return 'Account is blank'
}
if (!psw) {
return 'Password is blank'
}
const davAuth: WebDAVAuth = { type: 'password', username: acc, password: psw }
const davContext: WebDAVContext = { endpoint, auth: davAuth }
const webDavPath = processDir(dirPath)
try {
const exist = await judgeDirExist(davContext, webDavPath)
if (!exist) {
return "Directory not found"
}
} catch (e) {
return (e as Error)?.message ?? e?.toString?.() ?? 'Unknown error'
}
}
async clear(context: timer.backup.CoordinatorContext<never>, client: timer.backup.Client): Promise<void> {
const cid = client.id
const dirPath = processDir(context.ext?.dirPath)
const davContext = prepareContext(context)
const clientDirPath = `${dirPath}${cid}/`
const exist = await judgeDirExist(davContext, clientDirPath)
if (!exist) return
await deleteDir(davContext, clientDirPath)
}
}