forked from moya-a/G-Drive-SharedFiles-Checker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchecker.js
297 lines (263 loc) · 10.5 KB
/
checker.js
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
const FOLDER_TYPE = 'D';
const FILE_TYPE = 'F';
const PROPERTY_KEY_FOR_SHEET_ID = "PROPERTY_KEY_FOR_SHEET_ID";
const NUMBER_OF_OUTPUT_COLUMNS = 13;
const MAX_RUNNING_TIME_MS = 5 * 60 * 1000;
var counter = 0;
const CHECK_PRIVATE_FILES = true; // change to false if you don't want to check 'PRIVATE' files, aka those which aren't shared with a link
const folderId = ""; // Define this to use a folder other than the user's root folder. Get the folder ID from the long chunk of random numbers/letters in the URL when you navigate to the folder
// List of domains which should be considered "internal"
var internalDomains = [
];
// List of users who are outside our domain but still considered "internal"
const internalUsers = [
];
function main() {
const results = [];
setupInternalDomains();
const rootFolder = selectFolder();
const sheet = loadOrCreateSheetInSpreadsheet();
var finishedExecution = false;
try {
// Recursively loop through all files, with resume support
finishedExecution = processRootFolder(rootFolder, (fileOrFolder,path, type) => processFileOrFolder(fileOrFolder, path, type, results, sheet));
}
finally { // In case an unexpected error is thrown, flush our results
flushResultsToSheet(sheet, results);
Logger.log("Flushed all results!")
}
deleteAllTriggers();
if (!finishedExecution) {
Logger.log(`Did not finish execution, therefore setting up trigger to rerun. ${counter} files processed this round.`);
scheduleRun();
} else {
PropertiesService.getDocumentProperties().deleteProperty(PROPERTY_KEY_FOR_SHEET_ID)
Logger.log(`Finished processing all files! ${counter} files were processed this round.`);
}
}
function restartFully() {
PropertiesService.getDocumentProperties().deleteAllProperties()
}
function scheduleRun() {
ScriptApp.newTrigger('main')
.timeBased()
.after(1000)
.create();
}
function processFileOrFolder(file, parentPath, type, results, sheet) {
counter++;
const filePath = parentPath + '/' + file.getName();
console.time("processFileOrFolder");
try {
const sharingAccess = file.getSharingAccess();
if (CHECK_PRIVATE_FILES || DriveApp.Access.PRIVATE != sharingAccess) {
const editors = file.getEditors();
const viewers = file.getViewers();
const listEditors = editors.map(it => it.getEmail()).join(', ');
const listViewers = viewers.map(it => it.getEmail()).join(', ');
const listExternalEditors = editors.filter(isNotInternalUser).map(it => it.getEmail()).join(', ');
const listExternalViewers = viewers.filter(isNotInternalUser).map(it => it.getEmail()).join(', ');
const fileData = [
'ok',
filePath,
sharingAccess,
file.getSharingPermission(),
file.getOwner().getEmail(),
listEditors,
listViewers,
listExternalEditors,
listExternalViewers,
file.getDateCreated(),
file.getSize(),
file.getUrl(),
FILE_TYPE == type ? file.getMimeType() : 'Folder',
];
results.push(fileData);
}
} catch (err) {
Logger.log('Error while analyzing file %s : %s', filePath, err)
const fileData = [
err,
filePath,
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
];
results.push(fileData);
}
console.timeEnd("processFileOrFolder");
if (results.length >= 20) {
flushResultsToSheet(sheet, results);
}
}
function loadOrCreateSheetInSpreadsheet() {
const sheetId = JSON.parse(PropertiesService.getDocumentProperties().getProperty(PROPERTY_KEY_FOR_SHEET_ID));
if (sheetId == null) {
var newSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
//var newSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(`Folder:${rootFolder.getName()}`);
newSheet.appendRow(["Status", "Path", "Access", "Permissions", "Owner", "Editors", "Viewers", "ExternalEditors", "ExternalViewers", "Date", "Size", "URL", "Type"]);
PropertiesService.getDocumentProperties().setProperty(PROPERTY_KEY_FOR_SHEET_ID, JSON.stringify(newSheet.getSheetId()));
return newSheet;
} else {
var foundSheet = getSheetById(SpreadsheetApp.getActiveSpreadsheet(), sheetId);
if(foundSheet) {
return foundSheet;
} else {
throw "Failed to resume, failed ot find sheet from previous run."
}
}
}
function flushResultsToSheet(sheet, results) {
console.time("flushResultsToSheet");
Logger.log(`Flushing ${results.length} rows to the sheet`)
if(results.length > 0) {
// Don't use appendRow which takes 800ms for each row, instead batch insert.
// Note, Google will sporadically fail basically any call at all. In the past this function has temporarily failed.
// To make this more resilient we could wrap all Google functions in a wait retry loop.
sheet.getRange(sheet.getLastRow() + 1, 1, results.length, NUMBER_OF_OUTPUT_COLUMNS).setValues(results);
// In JS will clear the array without losing the reference to it.
results.length = 0;
}
console.timeEnd("flushResultsToSheet");
}
function selectFolder() {
if (folderId == "") {
return DriveApp.getRootFolder();
} else {
return DriveApp.getFolderById(folderId);
}
}
function setupInternalDomains() {
if(internalDomains.length == 0) {
const currentUserDomain = Session.getEffectiveUser().getEmail().split("@")[1];
if (currentUserDomain != "gmail.com") {
internalDomains.push(Session.getEffectiveUser().getEmail().split("@")[1]);
}
}
Logger.log('Considering users at the following domains to be internal users');
Logger.log(internalDomains)
}
function isNotInternalUser(user) {
if (internalUsers.includes(user.getEmail())) return false;
if (internalDomains.includes(user.getDomain())) return false;
return true;
}
// From https://developers.google.com/apps-script/reference/script/script-app#deletetriggertrigger
function deleteAllTriggers() {
// Deletes all triggers in the current project.
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
Logger.log(`Deleted all triggers.`);
}
// From https://stackoverflow.com/a/68092341
function getSheetById(ss, sheetId) {
var foundSheets = ss.getSheets().filter(sheet => sheet.getSheetId() === sheetId);
return foundSheets.length ? foundSheets[0] : undefined;
}
// Folder & File iteration based on https://stackoverflow.com/a/54104948
function processRootFolder(rootFolder, callback, timeoutCallback) {
var RECURSIVE_ITERATOR_KEY = "RECURSIVE_ITERATOR_KEY";
var startTime = (new Date()).getTime();
// [{folderName: String, fileIteratorContinuationToken: String?, folderIteratorContinuationToken: String}]
var recursiveIterator = JSON.parse(PropertiesService.getDocumentProperties().getProperty(RECURSIVE_ITERATOR_KEY));
if (recursiveIterator !== null) {
// verify that it's actually for the same folder
if (rootFolder.getName() !== recursiveIterator[0].folderName) {
console.warn("Looks like this is a new folder. Clearing out the old iterator.");
recursiveIterator = null;
} else {
console.info("Resuming session.");
}
}
if (recursiveIterator === null) {
console.info("Starting new session.");
recursiveIterator = [];
recursiveIterator.push(makeIterationFromFolder(rootFolder));
}
while (recursiveIterator.length > 0) {
recursiveIterator = nextIteration(recursiveIterator, callback);
var currTime = (new Date()).getTime();
var elapsedTimeInMS = currTime - startTime;
var timeLimitExceeded = elapsedTimeInMS >= MAX_RUNNING_TIME_MS;
if (timeLimitExceeded) {
PropertiesService.getDocumentProperties().setProperty(RECURSIVE_ITERATOR_KEY, JSON.stringify(recursiveIterator));
console.info("Stopping loop after '%d' milliseconds. Please continue running.", elapsedTimeInMS);
return false;
}
}
console.info("Done running");
PropertiesService.getDocumentProperties().deleteProperty(RECURSIVE_ITERATOR_KEY);
return true;
}
// process the next file or folder
function nextIteration(recursiveIterator, callback) {
var currentIteration = recursiveIterator[recursiveIterator.length-1];
if (currentIteration.fileIteratorContinuationToken !== null) {
try {
var fileIterator = DriveApp.continueFileIterator(currentIteration.fileIteratorContinuationToken);
if (fileIterator.hasNext()) {
// process the next file
var path = recursiveIterator.map(function(iteration) { return iteration.folderName; }).join("/");
callback(fileIterator.next(), path, FILE_TYPE);
currentIteration.fileIteratorContinuationToken = fileIterator.getContinuationToken();
recursiveIterator[recursiveIterator.length-1] = currentIteration;
return recursiveIterator;
} else {
// done processing files
currentIteration.fileIteratorContinuationToken = null;
recursiveIterator[recursiveIterator.length-1] = currentIteration;
return recursiveIterator;
}
}
catch (err) {
Logger.log(`Error while iterating fileIteratorContinuationToken: ${currentIteration.fileIteratorContinuationToken} fileIterator: ${fileIterator}`);
Logger.log(err);
Logger.log("Error Stack:");
Logger.log(err.stack)
throw err;
}
}
if (currentIteration.folderIteratorContinuationToken !== null) {
var folderIterator = DriveApp.continueFolderIterator(currentIteration.folderIteratorContinuationToken);
try {
if (folderIterator.hasNext()) {
// process the next folder
var folder = folderIterator.next();
var path = recursiveIterator.map(function(iteration) { return iteration.folderName; }).join("/");
callback(folder, path, FOLDER_TYPE)
recursiveIterator[recursiveIterator.length-1].folderIteratorContinuationToken = folderIterator.getContinuationToken();
recursiveIterator.push(makeIterationFromFolder(folder));
return recursiveIterator;
} else {
// done processing subfolders
recursiveIterator.pop();
return recursiveIterator;
}
}
catch (err) {
Logger.log(`Error while iterating folderIteratorContinuationToken: ${currentIteration.folderIteratorContinuationToken} folderIterator: ${folderIterator}`);
Logger.log(err);
Logger.log("Error Stack:");
Logger.log(err.stack)
throw err;
}
}
throw "should never get here";
}
function makeIterationFromFolder(folder) {
return {
folderName: folder.getName(),
fileIteratorContinuationToken: folder.getFiles().getContinuationToken(),
folderIteratorContinuationToken: folder.getFolders().getContinuationToken()
};
}