TODOList

Форк
0
/
main.js 
323 строки · 10.6 Кб
1
// Description: Main file for start server
2
// This file is part of the "Todo app" project
3
// Author: ivanvit100 @ GitHub
4
// Licence: MIT
5

6
const { app, BrowserWindow } = require('electron');
7
const fs = require('fs');
8
const os = require('os');
9
const path = require('path');
10
const session = require('express-session');
11
const express = require('express');
12
const bodyParser = require('body-parser');
13

14
// TODO: logging
15

16
let user;
17
let programFilesPath;
18
let lang;
19
let clientLang;
20

21
// Prepare directories and files
22
// If the directories and files are not found, they will be created
23
// Input: none
24
// Output: none
25
if (os.platform() === 'win32') 
26
    programFilesPath = 'C:\\Program Files\\.todo\\';
27
else if (os.platform() === 'linux')
28
    programFilesPath = path.join(os.homedir(), '.todo/');
29

30
const filePath = path.join(programFilesPath, 'taskManager/default.json');
31
const fileDir = path.dirname(filePath);
32
if (!fs.existsSync(programFilesPath))
33
    fs.mkdirSync(programFilesPath, { recursive: true });
34
if (!fs.existsSync(fileDir))
35
    fs.mkdirSync(fileDir, { recursive: true });
36

37
if (!fs.existsSync(filePath))
38
    fs.writeFileSync(filePath, '{"data":[{"name":"GitHub","done":false,"description":"https://github.com/ivanvit100/TODOList","date":null,"lvl":9}]}', 'utf8');
39
    
40
// Load config or create new one
41
// Input: config.json
42
// Output: user = {
43
//   'color-date-alert': bool,
44
//   'lang': string,
45
//   'rewrite-config': bool,
46
//   'login': string,
47
//   'password': string,
48
//   'key': string
49
// }
50
try {
51
    const data = JSON.parse(fs.readFileSync(path.join(programFilesPath, 'config.json')));
52
    user = data['todo'];
53
    if (!['color-date-alert', 'lang', 'rewrite-config', 'login', 'password', 'key'].every(key => key in user)) {
54
        throw new Error();
55
    }
56
} catch (error) {
57
    user = {
58
        'color-date-alert': true,
59
        'lang': 'ru',
60
        'rewrite-config': true,
61
        'login': 'admin',
62
        'password': 'admin',
63
        'key': 'secret'
64
    };
65
    fs.writeFileSync(path.join(programFilesPath, 'config.json'), JSON.stringify({todo: user}));
66
    console.error('Error reading config file, default settings are used');
67
}
68
    
69
// Load language file
70
// If the file is not found, the default language will be used
71
// Default language is "ru"
72
// Input: /server/langs/lang.{user["lang"]}.json
73
// Output: lang = {...}
74
try {
75
    lang = JSON.parse(fs.readFileSync(`${__dirname}/server/langs/lang.${user["lang"]}.json`));
76
    clientLang = JSON.parse(fs.readFileSync(`${__dirname}/server/langs/client.${user["lang"]}.json`));
77
} catch (error) {
78
    lang = JSON.parse(fs.readFileSync(`${__dirname}/server/langs/lang.ru.json`));
79
    clientLang = JSON.parse(fs.readFileSync(`${__dirname}/server/langs/client.ru.json`));
80
    console.error('Error reading lang file, default settings are used');
81
}
82

83
// Create an express application
84
// Use the express.json() middleware to parse JSON data
85
var encoder = bodyParser.json();
86
const expressApp = express();
87
expressApp.use(express.json());
88
expressApp.use(session({
89
    secret: user['key'],
90
    resave: false,
91
    saveUninitialized: true
92
}));
93

94
// Path to static files
95
// Input: /public/{filename} (string)
96
// Output: {filename} (file))
97
expressApp.get('/public/:filename', encoder, (req, res) => {
98
    res.sendFile(path.join(__dirname, 'public', req.params.filename), err => {
99
        if (err) {
100
            console.error(`[path]: Error reading file "${req.params.filename}"`);
101
            res.sendFile(path.join(__dirname, 'public', '404.html'));
102
        }
103
    });
104
});
105

106
// Function to send config to the client
107
// Automatically excludes login and password from issuance
108
// Input: none
109
// Output: status: string, message: object
110
expressApp.post('/api/config',  encoder, (req, res) => {
111
    const data = {
112
        'color-date-alert': user['color-date-alert'],
113
        'lang': clientLang
114
    };
115
    const response = {
116
        'status': 'success',
117
        'login': req.session && req.session.login ? req.session.login : false,
118
        'message': data
119
    };
120
    res.json(response);
121
});
122

123
// Main user authentification
124
// Called by the client for the purpose of initial verification 
125
// and further request of taskList's list
126
// Input: login: string, password: string
127
// Output: status: string, message: string
128
// "status" is an additional class of the notification window on the client side
129
// It can be "success", "error" or "info"
130
// "message" is the text of the notification window on the client side
131
expressApp.post('/api/auth', encoder, (req, res) => {
132
    const data = req.body;
133
    let response;
134
    if ((data['login'] == user['login'] && data['password'] == user['password']) || (user['login'].length == 0) || getCookie(req, 'login')){
135
        res.setHeader('Set-Cookie', `login=${data['login']}; Max-Age=900000; HttpOnly; SameSite=None; Secure`);
136
        response = {
137
            'status': 'success',
138
            'message': lang['auth-success']
139
        };
140
    } else if(data['login'].length != 0) {
141
        response = {
142
            'status': 'error',
143
            'message': lang['auth-error']
144
        };
145
    }
146
    res.send(response);
147
});
148

149
// Get data of taskList (array of tasks)
150
// Input: login: string, password: string, taskList: string
151
// Output: status: string, data: array | message: string
152
// login and password are used for additional verification
153
expressApp.post('/api/getTaskList', (req, res) => {
154
    const requestData = req.body;
155
    let response;
156
    if (getCookie(req, 'login')) {
157
        try {
158
            const data = JSON.parse(fs.readFileSync(path.join(programFilesPath, `taskManager/${requestData["taskList"]}.json`)));
159
            response = {
160
                'status': 'success',
161
                'message': data
162
            };
163
        } catch (error) {
164
            console.error(`[getTaskList]: Error reading file "${requestData["taskList"]}.json"`);
165
            response = {
166
                'status': 'error',
167
                'message': lang['file-error']
168
            };
169
        }
170
    } else {
171
        response = {
172
            'status': 'error',
173
            'message': lang['auth-error']
174
        };
175
        console.warn('[getTaskList]: Error authentication');
176
    }
177
    res.json(response);
178
});
179

180
// Get list of taskLists (array of taskLists)
181
// Input: login: string, password: string
182
// Output: status: string, data: array | message: string
183
// login and password are used for additional verification
184
expressApp.post('/api/getTaskListList', (req, res) => {
185
    let response;
186
    if(getCookie(req, 'login')){
187
        try {
188
            const files = fs.readdirSync(path.join(programFilesPath, 'taskManager'));
189
            const data = files.filter(file => file.endsWith('.json')).map(file => path.basename(file, '.json'));
190
            response = {
191
                'status': 'success',
192
                'message': data
193
            };
194
        } catch (error) {
195
            console.error('[getTaskListList]: Error reading files');
196
            response = {
197
                'status': 'error',
198
                'message': lang['file-error']
199
            };
200
        }
201
    } else {
202
        response = {
203
            'status': 'error',
204
            'message': lang['auth-error']
205
        };
206
        console.warn('[getTaskListList]: Error authentication');
207
    }
208
    res.json(response);
209
});
210

211
// Save data of taskList (array of tasks)
212
// Can be used for updating information about one task
213
// or for creating a new taskList
214
// Input: login: string, password: string, taskList: string, data: object
215
// Output: status: string, message: string
216
expressApp.post('/api/saveTaskList', (req, res) => {
217
    const requestData = req.body;
218
    let response;
219
    console.log(requestData["data"]);
220
    if (getCookie(req, 'login')) {
221
        try {
222
            fs.writeFileSync(`${programFilesPath}/taskManager/${requestData["taskList"]}.json`, JSON.stringify(requestData["data"]));
223
            response = {
224
                'status': 'success',
225
                'message': lang['save-success']
226
            };
227
        } catch (error) {
228
            console.error(`[saveTaskList]: Error saving file "${requestData["taskList"]}.json because of "${error}"`);
229
            response = {
230
                'status': 'error',
231
                'message': lang['save-error']
232
            };
233
        }
234
    } else {
235
        response = {
236
            'status': 'error',
237
            'message': lang['auth-error']
238
        };
239
        console.warn('[saveTaskList]: Error authentication');
240
    }
241
    res.json(response);
242
});
243

244
// Function to delete taskList
245
// Its will delete /taskManager/{taskList}.json
246
// Input: login: string, password: string, taskList: string
247
// Output: status: string, message: string
248
expressApp.post('/api/deleteList', (req, res) => {
249
    const data = req.body;
250
    let response;
251
    if (req.session && req.session.login === user['login']) {
252
        const taskList = data['taskList'];
253
        try {
254
            fs.unlinkSync(path.join(programFilesPath,`taskManager/${taskList}.json`));
255
            response = {
256
                'status': 'success',
257
                'message': lang['delete-success']
258
            };
259
        } catch (error) {
260
            console.error(`[deleteList]: Error deleting file "${taskList}.json"`);
261
            response = {
262
                'status': 'error',
263
                'message': lang['delete-error']
264
            };
265
        }
266
    } else {
267
        response = {
268
            'status': 'error',
269
            'message': lang['auth-error']
270
        };
271
        console.warn('[deleteList]: Error authentication');
272
    }
273
    res.json(response);
274
});
275

276
// Path responsible for non-existent routes
277
// Input: none
278
// Output: 404.html
279
expressApp.get('*', (req, res) => {
280
    console.warn(`[path]: Unknown path "${req.path}" catched`);
281
    res.sendFile(path.join(__dirname, 'public', '404.html'));
282
});
283

284
// Creating an application and launching a viewer window
285
// The server is started on port 3000
286
// Input: none
287
// Output: none
288
const server = expressApp.listen(3000, () => console.log('Server started on port 3000'));
289
app.whenReady().then(() => {
290
    const window = new BrowserWindow({
291
        width: 1280,
292
        height: 720,
293
        webPreferences: {
294
            nodeIntegration: true,
295
            contextIsolation: false,
296
            nativeWindowOpen: true,
297
            webviewTag: true,
298
            enableRemoteModule: true
299
        }
300
    });
301
    
302
    window.loadFile("./public/index.html");
303
});
304

305
// Closing the application
306
// Input: none
307
// Output: none
308
app.on('window-all-closed', () => {
309
    server.close();
310
    app.quit();
311
});
312

313
// Function to get cookie and validate it
314
// Input: req: object, name: string
315
// Output: bool
316
getCookie = (req, name) => {
317
    const cookies = req.headers.cookie ? req.headers.cookie.split('; ').reduce((prev, current) => {
318
        const [name, value] = current.split('=');
319
        prev[name] = value;
320
        return prev;
321
    }, {}) : {};
322
    return cookies[name] == user[name];
323
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.