├── README.md
├── README_zh.md
├── index.js
├── themes
├── classic
│ └── app.js
├── logo.png
└── material
│ └── app.js
└── 使用及免责协议.md
/README.md:
--------------------------------------------------------------------------------
1 | 前几天我突然发现自己的 goindex 网页打开是一片空白,然后才知道 donwa 大佬删库了,于是我找了其他的仓库部署了一下网页,今天偶然发现网页打不开是有解决办法的,看到了一篇博文,所以就赶快修改了自己的代码,非常感谢,下面是解决方法,另外博文参考自:
2 | - 解决goindex作者删库,导致goindex打不开的问题 - 天下无鱼 https://shikey.com/2020/04/27/goindex-index-js-repack.html
3 | ## 解决方法:
4 | - 【可选】首先到 GitHub Fork一份 Goindex 的代码。
5 | - 登录CF,打开workers,选中项目修改原代码部分的一行即可。具体操作如下 —— 找到以下代码,一般是在 21行/23行 。
6 |
7 | 
8 | !图片加载不出来可直接访问:http://dwz.date/awtQ
9 |
10 | ### 另外我发现 goindex 现在的版本可以看 GD 快捷方式的文件了,这样就可以把别人共享的文件直接创建快捷方式,在自己的网页看,非常 nice 。
11 |
12 | PS:以下内容为原始内容。
13 |
14 | ---
15 |
16 | 
17 |
18 | GoIndex
19 | ====
20 | Google Drive Directory Index
21 | Combining the power of [Cloudflare Workers](https://workers.cloudflare.com/) and [Google Drive](https://www.google.com/drive/) will allow you to index you files on the browser on Cloudflare Workers.
22 |
23 | `index.js` is the content of the Workers script.
24 |
25 | ## Demo
26 | material: [https://index.gd.workers.dev/](https://index.gd.workers.dev/)
27 | classic: [https://indexc.gd.workers.dev/](https://indexc.gd.workers.dev/)
28 |
29 | ## Deployment
30 | 1.Install `rclone` software locally
31 | 2.Follow [https://rclone.org/drive/]( https://rclone.org/drive/) bind a drive
32 | 3.Execute the command`rclone config file` to find the file `rclone.conf` path
33 | 4.Open `rclone.conf`,find the configuration `root_folder_id` and `refresh_token`
34 | 5.Download index.js in https://github.com/donwa/goindex and fill in root and refresh_token
35 | 6.Deploy the code to [Cloudflare Workers](https://www.cloudflare.com/)
36 |
37 | ## Quick Deployment
38 | 1.Open https://installen.gd.workers.dev/
39 | 2.Auth and get the code
40 | 3.Deploy the code to [Cloudflare Workers](https://www.cloudflare.com/)
41 |
42 |
43 |
44 | ## About
45 | Cloudflare Workers allow you to write JavaScript which runs on all of Cloudflare's 150+ global data centers.
46 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | GoIndex
4 | ====
5 | 基于 [Cloudflare Workers](https://workers.cloudflare.com/) 和 [Google Drive](https://www.google.com/drive/) 的功能,你可以部署你的代码在Cloudflare Workers,实现以目录形式展示google drive中的文件。
6 |
7 | `index.js` 包含 Workers 所需的代码.
8 |
9 | ## Demo
10 | material:
11 | [https://index.gd.workers.dev/](https://index.gd.workers.dev/)
12 | classic:
13 | [https://indexc.gd.workers.dev/](https://indexc.gd.workers.dev/)
14 |
15 | ## 安装部署方案1
16 | 1、在本地安装 rclone
17 | 2、按照 https://rclone.org/drive/ 流程进行授权。
18 | 3、执行 rclone config file 查看 rclone.conf 路径。找到root_folder_id和refresh_token记录下来。
19 | 4、下载 https://github.com/donwa/goindex 中的 index.js 并填入 root 和 refresh_token
20 | 5、复制代码 到 CloudFlare 部署。
21 |
22 | ## 安装部署方案2
23 | 作者不会记录refresh_token,但为避免纠纷,建议有条件的同学使用方案1进行部署
24 | 1、访问[https://install.gd.workers.dev/](https://install.gd.workers.dev/)
25 | 2、授权认证后,生成部署代码。
26 | 3、复制代码 到 CloudFlare 部署。
27 |
28 | ## 文件夹密码:
29 | 在google drive 文件中放置 `.password` 文件来设置密码。
30 | 密码文件只能保护该文件不被列举,不能保护该文件夹的子文件夹不被列举。
31 | 也不保护文件夹下文件不被下载。
32 |
33 | 程序文件中 `root_pass` 只为根目录密码,优先于 `.password` 文件
34 |
35 |
36 | ## 更新日志
37 |
38 | 1.0.6
39 | 添加 classic 模板
40 |
41 | 1.0.5
42 | 添加文件展示页
43 |
44 | 1.0.4
45 | 修复 注入问题。
46 |
47 | 1.0.3
48 | 修复 `.password` 绕过下载问题。
49 |
50 | 1.0.2
51 | 优化前端逻辑
52 | 添加文件预览功能(临时)
53 | 添加前端文件缓存功能
54 |
55 | 1.0.1
56 | 添加 README.md 、 HEAD.md 支持
57 |
58 | 1.0.0
59 | 前后端分离,确定基本架构
60 | 添加.password 支持
61 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // https://github.com/donwa/goindex/blob/master/使用及免责协议.md
2 |
3 | var authConfig = {
4 | "siteName": "GoIndex", // 网站名称
5 | "root_pass": "index", // 根目录密码,优先于.password
6 | "version" : "1.0.6", // 程序版本
7 | "theme" : "material", // material classic
8 | "client_id": "202264815644.apps.googleusercontent.com",
9 | "client_secret": "X4Z3ca8xfWDb1Voo-F9a7ZxJ",
10 | "refresh_token": "", // 授权 token
11 | "root": "root" // 根目录ID
12 | };
13 |
14 | var gd;
15 |
16 | var html = `
17 |
18 |
19 |
20 |
21 |
22 | ${authConfig.siteName}
23 |
24 |
25 |
26 |
27 |
28 | `;
29 |
30 | addEventListener('fetch', event => {
31 | event.respondWith(handleRequest(event.request));
32 | });
33 |
34 | /**
35 | * Fetch and log a request
36 | * @param {Request} request
37 | */
38 | async function handleRequest(request) {
39 | if(gd == undefined){
40 | gd = new googleDrive(authConfig);
41 | }
42 |
43 | if(request.method == 'POST'){
44 | return apiRequest(request);
45 | }
46 |
47 | let url = new URL(request.url);
48 | let path = url.pathname;
49 | let action = url.searchParams.get('a');
50 |
51 | if(path.substr(-1) == '/'){
52 | try {
53 | await gd.list(path);
54 | } catch (e) {
55 | return new Response("", { status: 404 }); // if path: /notexist/
56 | }
57 | return new Response(html, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' } });
58 | } else if(action != null){
59 | if (await gd.file(path) == undefined){
60 | return new Response(html404, { status: 404, headers: { 'Content-Type': 'text/html; charset=utf-8' } });
61 | }
62 | return new Response(html, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' } });
63 | } else {
64 | if (path.split('/').pop().toLowerCase() == ".password") {
65 | return new Response("", { status: 404 });
66 | }
67 | try {
68 | await gd.file(path);
69 | } catch (e) {
70 | return new Response("", { status: 404 }); // if path: /notexist/notexist
71 | }
72 | let file = await gd.file(path);
73 | if (file == undefined){
74 | return new Response("", { status: 404 }); // if path: /exist/notexist
75 | }
76 |
77 | let range = request.headers.get('Range');
78 | return gd.down(file.id, range);
79 | }
80 | }
81 |
82 |
83 | async function apiRequest(request) {
84 | let url = new URL(request.url);
85 | let path = url.pathname;
86 |
87 | let option = {status:200,headers:{'Access-Control-Allow-Origin':'*'}}
88 |
89 | if(path.substr(-1) == '/'){
90 | // check password
91 | let password = await gd.password(path);
92 | console.log("dir password", password);
93 | if(password != undefined && password != null && password != ""){
94 | try{
95 | var obj = await request.json();
96 | }catch(e){
97 | var obj = {};
98 | }
99 | console.log(password,obj);
100 | if(password.replace("\n", "") != obj.password){
101 | let html = `{"error": {"code": 401,"message": "password error."}}`;
102 | return new Response(html,option);
103 | }
104 | }
105 | let list = await gd.list(path);
106 | return new Response(JSON.stringify(list),option);
107 | }else{
108 | let file = await gd.file(path);
109 | let range = request.headers.get('Range');
110 | return new Response(JSON.stringify(file));
111 | }
112 | }
113 |
114 | class googleDrive {
115 | constructor(authConfig) {
116 | this.authConfig = authConfig;
117 | this.paths = [];
118 | this.files = [];
119 | this.passwords = [];
120 | this.paths["/"] = authConfig.root;
121 | if(authConfig.root_pass != ""){
122 | this.passwords["/"] = authConfig.root_pass;
123 | }
124 | this.accessToken();
125 | }
126 |
127 | async down(id, range=''){
128 | let url = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
129 | let requestOption = await this.requestOption();
130 | requestOption.headers['Range'] = range;
131 | return await fetch(url, requestOption);
132 | }
133 |
134 | async file(path){
135 | if(typeof this.files[path] == 'undefined'){
136 | this.files[path] = await this._file(path);
137 | }
138 | return this.files[path] ;
139 | }
140 |
141 | async _file(path){
142 | let arr = path.split('/');
143 | let name = arr.pop();
144 | name = decodeURIComponent(name).replace(/\'/g, "\\'");
145 | let dir = arr.join('/')+'/';
146 | console.log(name, dir);
147 | let parent = await this.findPathId(dir);
148 | console.log(parent);
149 | let url = 'https://www.googleapis.com/drive/v3/files';
150 | let params = {'includeItemsFromAllDrives':true,'supportsAllDrives':true};
151 | params.q = `'${parent}' in parents and name = '${name}' andtrashed = false`;
152 | params.fields = "files(id, name, mimeType, size ,createdTime, modifiedTime, iconLink, thumbnailLink, shortcutDetails)";
153 | url += '?'+this.enQuery(params);
154 | let requestOption = await this.requestOption();
155 | let response = await fetch(url, requestOption);
156 | let obj = await response.json();
157 | if (obj.files && obj.files[0] && obj.files[0].mimeType == 'application/vnd.google-apps.shortcut'){
158 | obj.files[0].id = obj.files[0].shortcutDetails.targetId;
159 | obj.files[0].mimeType = obj.files[0].shortcutDetails.targetMimeType;
160 | }
161 | console.log(obj);
162 | return obj.files[0];
163 | }
164 |
165 | // 通过reqeust cache 来缓存
166 | async list(path){
167 | if (gd.cache == undefined) {
168 | gd.cache = {};
169 | }
170 |
171 | if (gd.cache[path]) {
172 | return gd.cache[path];
173 | }
174 |
175 | let id = await this.findPathId(path);
176 | var obj = await this._ls(id);
177 | if (obj.files && obj.files.length > 1000) {
178 | gd.cache[path] = obj;
179 | }
180 |
181 | return obj
182 | }
183 |
184 | async password(path){
185 | if(this.passwords[path] !== undefined){
186 | return this.passwords[path];
187 | }
188 |
189 | console.log("load",path,".password",this.passwords[path]);
190 |
191 | let file = await gd.file(path+'.password');
192 | if(file == undefined){
193 | this.passwords[path] = null;
194 | }else{
195 | let url = `https://www.googleapis.com/drive/v3/files/${file.id}?alt=media`;
196 | let requestOption = await this.requestOption();
197 | let response = await this.fetch200(url, requestOption);
198 | this.passwords[path] = await response.text();
199 | }
200 |
201 | return this.passwords[path];
202 | }
203 |
204 | async _ls(parent){
205 | console.log("_ls",parent);
206 |
207 | if(parent==undefined){
208 | return null;
209 | }
210 | const files = [];
211 | let pageToken;
212 | let obj;
213 | let params = {'includeItemsFromAllDrives':true,'supportsAllDrives':true};
214 | params.q = `'${parent}' in parents and trashed = false AND name !='.password'`;
215 | params.orderBy= 'folder,name,modifiedTime desc';
216 | params.fields = "nextPageToken, files(id, name, mimeType, size , modifiedTime, shortcutDetails)";
217 | params.pageSize = 1000;
218 |
219 | do {
220 | if (pageToken) {
221 | params.pageToken = pageToken;
222 | }
223 | let url = 'https://www.googleapis.com/drive/v3/files';
224 | url += '?'+this.enQuery(params);
225 | let requestOption = await this.requestOption();
226 | let response = await fetch(url, requestOption);
227 | obj = await response.json();
228 | obj.files.forEach(file => {
229 | if (file && file.mimeType == 'application/vnd.google-apps.shortcut') {
230 | file.id = file.shortcutDetails.targetId;
231 | file.mimeType = file.shortcutDetails.targetMimeType;
232 | }
233 | });
234 | files.push(...obj.files);
235 | pageToken = obj.nextPageToken;
236 | } while (pageToken);
237 |
238 | obj.files = files;
239 | return obj;
240 | }
241 |
242 | async findPathId(path){
243 | let c_path = '/';
244 | let c_id = this.paths[c_path];
245 |
246 | let arr = path.trim('/').split('/');
247 | for(let name of arr){
248 | c_path += name+'/';
249 |
250 | if(typeof this.paths[c_path] == 'undefined'){
251 | let id = await this._findDirId(c_id, name);
252 | this.paths[c_path] = id;
253 | }
254 |
255 | c_id = this.paths[c_path];
256 | if(c_id == undefined || c_id == null){
257 | break;
258 | }
259 | }
260 | console.log(this.paths);
261 | return this.paths[path];
262 | }
263 |
264 | async _findDirId(parent, name){
265 | name = decodeURIComponent(name).replace(/\'/g, "\\'");
266 |
267 | console.log("_findDirId",parent,name);
268 |
269 | if(parent==undefined){
270 | return null;
271 | }
272 |
273 | let url = 'https://www.googleapis.com/drive/v3/files';
274 | let params = {'includeItemsFromAllDrives':true,'supportsAllDrives':true};
275 | params.q = `'${parent}' in parents and (mimeType = 'application/vnd.google-apps.folder' or mimeType = 'application/vnd.google-apps.shortcut') and name = '${name}' and trashed = false`;
276 | params.fields = "nextPageToken, files(id, name, mimeType, shortcutDetails)";
277 | url += '?'+this.enQuery(params);
278 | let requestOption = await this.requestOption();
279 | let response = await fetch(url, requestOption);
280 | let obj = await response.json();
281 | if(obj.files[0] == undefined){
282 | return null;
283 | }
284 | if (obj.files[0].mimeType == 'application/vnd.google-apps.shortcut' && obj.files[0].shortcutDetails.targetMimeType == 'application/vnd.google-apps.folder') {
285 | obj.files[0].id = obj.files[0].shortcutDetails.targetId;
286 | } else if (obj.files[0].mimeType == 'application/vnd.google-apps.shortcut' && obj.files[0].shortcutDetails.targetMimeType != 'application/vnd.google-apps.folder'){
287 | return null;
288 | }
289 | return obj.files[0].id;
290 | }
291 |
292 | async accessToken(){
293 | console.log("accessToken");
294 | if(this.authConfig.expires == undefined ||this.authConfig.expires< Date.now()){
295 | const obj = await this.fetchAccessToken();
296 | if(obj.access_token != undefined){
297 | this.authConfig.accessToken = obj.access_token;
298 | this.authConfig.expires = Date.now()+3500*1000;
299 | }
300 | }
301 | return this.authConfig.accessToken;
302 | }
303 |
304 | async fetchAccessToken() {
305 | console.log("fetchAccessToken");
306 | const url = "https://www.googleapis.com/oauth2/v4/token";
307 | const headers = {
308 | 'Content-Type': 'application/x-www-form-urlencoded'
309 | };
310 | const post_data = {
311 | 'client_id': this.authConfig.client_id,
312 | 'client_secret': this.authConfig.client_secret,
313 | 'refresh_token': this.authConfig.refresh_token,
314 | 'grant_type': 'refresh_token'
315 | }
316 |
317 | let requestOption = {
318 | 'method': 'POST',
319 | 'headers': headers,
320 | 'body': this.enQuery(post_data)
321 | };
322 |
323 | const response = await fetch(url, requestOption);
324 | return await response.json();
325 | }
326 |
327 | async fetch200(url, requestOption) {
328 | let response;
329 | for (let i = 0; i < 3; i++) {
330 | response = await fetch(url, requestOption);
331 | console.log(response.status);
332 | if (response.status != 403) {
333 | break;
334 | }
335 | await this.sleep(800 * (i + 1));
336 | }
337 | return response;
338 | }
339 |
340 | async requestOption(headers={},method='GET'){
341 | const accessToken = await this.accessToken();
342 | headers['authorization'] = 'Bearer '+ accessToken;
343 | return {'method': method, 'headers':headers};
344 | }
345 |
346 | enQuery(data) {
347 | const ret = [];
348 | for (let d in data) {
349 | ret.push(encodeURIComponent(d) + '=' + encodeURIComponent(data[d]));
350 | }
351 | return ret.join('&');
352 | }
353 |
354 | sleep(ms) {
355 | return new Promise(function (resolve, reject) {
356 | let i = 0;
357 | setTimeout(function () {
358 | console.log('sleep' + ms);
359 | i++;
360 | if (i >= 2) reject(new Error('i>=2'));
361 | else resolve(i);
362 | }, ms);
363 | })
364 | }
365 | }
366 |
367 | String.prototype.trim = function (char) {
368 | if (char) {
369 | return this.replace(new RegExp('^\\'+char+'+|\\'+char+'+$', 'g'), '');
370 | }
371 | return this.replace(/^\s+|\s+$/g, '');
372 | };
373 |
--------------------------------------------------------------------------------
/themes/classic/app.js:
--------------------------------------------------------------------------------
1 | document.write('');
2 | // 初始化页面,并载入必要资源
3 | function init(){
4 | document.siteName = $('title').html();
5 | $('body').addClass("mdui-theme-primary-blue-grey mdui-theme-accent-blue");
6 | var html = `
7 | Index of
8 |
10 | `;
11 | $('body').html(html);
12 | }
13 |
14 | function render(path){
15 | if(path.indexOf("?") > 0){
16 | path = path.substr(0,path.indexOf("?"));
17 | }
18 | title(path);
19 | nav(path);
20 | if(path.substr(-1) == '/'){
21 | list(path);
22 | }else{
23 | file(path);
24 | }
25 | }
26 |
27 |
28 | // 渲染 title
29 | function title(path){
30 | path = decodeURI(path);
31 | $('title').html(document.siteName+' - '+path);
32 | }
33 |
34 | // 渲染导航栏
35 | function nav(path){
36 | path = decodeURI(path);
37 | $('#heading').html('Index of '+path);
38 | }
39 |
40 | // 渲染文件列表
41 | function list(path){
42 | var content = `
43 | Name | Size | Date Modified |
44 | `;
45 |
46 | if(path != '/'){
47 | var up = path.split('/');
48 | up.pop();up.pop();
49 | up = up.join('/')+'/';
50 | content += `
51 |
52 |
53 | ..
54 | |
55 | |
56 | |
57 |
58 | `;
59 | }
60 | $('#table').html(content);
61 |
62 | var password = localStorage.getItem('password'+path);
63 | $.post(path,'{"password":"'+password+'"}', function(data,status){
64 | var obj = jQuery.parseJSON(data);
65 | if(typeof obj != 'null' && obj.hasOwnProperty('error') && obj.error.code == '401'){
66 | var pass = prompt("password","");
67 | localStorage.setItem('password'+path, pass);
68 | if(pass != null && pass != ""){
69 | list(path);
70 | }else{
71 | history.go(-1);
72 | }
73 | }else if(typeof obj != 'null'){
74 | list_files(path,obj.files);
75 | }
76 | });
77 | }
78 |
79 | function list_files(path,files){
80 | html = "";
81 | for(i in files){
82 | var item = files[i];
83 | if(item['size']==undefined){
84 | item['size'] = "";
85 | }
86 | item['modifiedTime'] = utc2beijing(item['modifiedTime']);
87 | item['size'] = formatFileSize(item['size']);
88 | if(item['mimeType'] == 'application/vnd.google-apps.folder'){
89 | var p = path+item.name+'/';
90 | html +=`
91 |
92 | ${item.name}/ |
93 | ${item['size']} |
94 | ${item['modifiedTime']} |
95 |
96 | `;
97 | }else{
98 | var p = path+item.name;
99 | html += `
100 |
101 | ${item.name} |
102 | ${item['size']} |
103 | ${item['modifiedTime']} |
104 |
105 | `;
106 | }
107 | }
108 | $('#table').append(html);
109 | }
110 |
111 | //时间转换
112 | function utc2beijing(utc_datetime) {
113 | // 转为正常的时间格式 年-月-日 时:分:秒
114 | var T_pos = utc_datetime.indexOf('T');
115 | var Z_pos = utc_datetime.indexOf('Z');
116 | var year_month_day = utc_datetime.substr(0,T_pos);
117 | var hour_minute_second = utc_datetime.substr(T_pos+1,Z_pos-T_pos-1);
118 | var new_datetime = year_month_day+" "+hour_minute_second; // 2017-03-31 08:02:06
119 |
120 | // 处理成为时间戳
121 | timestamp = new Date(Date.parse(new_datetime));
122 | timestamp = timestamp.getTime();
123 | timestamp = timestamp/1000;
124 |
125 | // 增加8个小时,北京时间比utc时间多八个时区
126 | var unixtimestamp = timestamp+8*60*60;
127 |
128 | // 时间戳转为时间
129 | var unixtimestamp = new Date(unixtimestamp*1000);
130 | var year = 1900 + unixtimestamp.getYear();
131 | var month = "0" + (unixtimestamp.getMonth() + 1);
132 | var date = "0" + unixtimestamp.getDate();
133 | var hour = "0" + unixtimestamp.getHours();
134 | var minute = "0" + unixtimestamp.getMinutes();
135 | var second = "0" + unixtimestamp.getSeconds();
136 | return year + "-" + month.substring(month.length-2, month.length) + "-" + date.substring(date.length-2, date.length)
137 | + " " + hour.substring(hour.length-2, hour.length) + ":"
138 | + minute.substring(minute.length-2, minute.length) + ":"
139 | + second.substring(second.length-2, second.length);
140 | }
141 |
142 | // bytes自适应转换到KB,MB,GB
143 | function formatFileSize(bytes) {
144 | if (bytes>=1000000000) {bytes=(bytes/1000000000).toFixed(2)+' GB';}
145 | else if (bytes>=1000000) {bytes=(bytes/1000000).toFixed(2)+' MB';}
146 | else if (bytes>=1000) {bytes=(bytes/1000).toFixed(2)+' KB';}
147 | else if (bytes>1) {bytes=bytes+' bytes';}
148 | else if (bytes==1) {bytes=bytes+' byte';}
149 | else {bytes='';}
150 | return bytes;
151 | }
152 |
153 | // 监听回退事件
154 | window.onpopstate = function(){
155 | var path = window.location.pathname;
156 | render(path);
157 | }
158 |
159 |
160 | $(function(){
161 | init();
162 | var path = window.location.pathname;
163 | $("body").on("click",'.folder',function(){
164 | var url = $(this).attr('href');
165 | history.pushState(null, null, url);
166 | render(url);
167 | return false;
168 | });
169 |
170 | render(path);
171 | });
172 |
--------------------------------------------------------------------------------
/themes/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZSChiao/goindex-backup/a0883228d41a2f25a14efa5c588149bbb776e0a2/themes/logo.png
--------------------------------------------------------------------------------
/themes/material/app.js:
--------------------------------------------------------------------------------
1 | // 在head 中 加载 必要静态
2 | document.write('');
3 | // markdown支持
4 | document.write('');
5 | document.write('');
6 |
7 | // 初始化页面,并载入必要资源
8 | function init(){
9 | document.siteName = $('title').html();
10 | $('body').addClass("mdui-theme-primary-blue-grey mdui-theme-accent-blue");
11 | var html = `
12 |
16 |
17 |
18 | `;
19 | $('body').html(html);
20 | }
21 |
22 | function render(path){
23 | if(path.indexOf("?") > 0){
24 | path = path.substr(0,path.indexOf("?"));
25 | }
26 | title(path);
27 | nav(path);
28 | if(path.substr(-1) == '/'){
29 | list(path);
30 | }else{
31 | file(path);
32 | }
33 | }
34 |
35 |
36 | // 渲染 title
37 | function title(path){
38 | path = decodeURI(path);
39 | $('title').html(document.siteName+' - '+path);
40 | }
41 |
42 | // 渲染导航栏
43 | function nav(path){
44 | var html = "";
45 | html += `${document.siteName}`;
46 | var arr = path.trim('/').split('/');
47 | var p = '/';
48 | if(arr.length > 0){
49 | for(i in arr){
50 | var n = arr[i];
51 | n = decodeURI(n);
52 | p += n+'/';
53 | if(n == ''){
54 | break;
55 | }
56 | html += `chevron_right${n}`;
57 | }
58 | }
59 | $('#nav').html(html);
60 | }
61 |
62 | // 渲染文件列表
63 | function list(path){
64 | var content = `
65 |
66 |
67 |
68 |
69 | -
70 |
71 | 文件
72 | expand_more
73 |
74 |
75 | 修改时间
76 | expand_more
77 |
78 |
79 | 大小
80 | expand_more
81 |
82 |
83 |
84 |
85 |
89 |
90 | `;
91 | $('#content').html(content);
92 |
93 | var password = localStorage.getItem('password'+path);
94 | $('#list').html(``);
95 | $('#readme_md').hide().html('');
96 | $('#head_md').hide().html('');
97 | $.post(path,'{"password":"'+password+'"}', function(data,status){
98 | var obj = jQuery.parseJSON(data);
99 | if(typeof obj != 'null' && obj.hasOwnProperty('error') && obj.error.code == '401'){
100 | var pass = prompt("目录加密, 请输入密码","");
101 | localStorage.setItem('password'+path, pass);
102 | if(pass != null && pass != ""){
103 | list(path);
104 | }else{
105 | history.go(-1);
106 | }
107 | }else if(typeof obj != 'null'){
108 | list_files(path,obj.files);
109 | }
110 | });
111 | }
112 |
113 | function list_files(path,files){
114 | html = "";
115 | for(i in files){
116 | var item = files[i];
117 | var p = path+item.name+'/';
118 | if(item['size']==undefined){
119 | item['size'] = "";
120 | }
121 |
122 | item['modifiedTime'] = utc2beijing(item['modifiedTime']);
123 | item['size'] = formatFileSize(item['size']);
124 | if(item['mimeType'] == 'application/vnd.google-apps.folder'){
125 | html +=`
126 |
127 | folder_open
128 | ${item.name}
129 |
130 | ${item['modifiedTime']}
131 | ${item['size']}
132 |
133 | `;
134 | }else{
135 | var p = path+item.name;
136 | var c = "file";
137 | if(item.name == "README.md"){
138 | get_file(p, item, function(data){
139 | markdown("#readme_md",data);
140 | });
141 | }
142 | if(item.name == "HEAD.md"){
143 | get_file(p, item, function(data){
144 | markdown("#head_md",data);
145 | });
146 | }
147 | var ext = p.split('.').pop();
148 | if("|html|php|css|go|java|js|json|txt|sh|md|mp4|webm|avi|bmp|jpg|jpeg|png|gif|m4a|mp3|wav|ogg|mpg|mpeg|mkv|rm|rmvb|mov|wmv|asf|ts|flv|".indexOf(`|${ext.toLowerCase()}|`) >= 0){
149 | p += "?a=view";
150 | c += " view";
151 | }
152 | html += `
153 |
154 | insert_drive_file
155 | ${item.name}
156 |
157 | ${item['modifiedTime']}
158 | ${item['size']}
159 |
160 | `;
161 | }
162 | }
163 | $('#list').html(html);
164 | }
165 |
166 |
167 | function get_file(path, file, callback){
168 | var key = "file_path_"+path+file['modifiedTime'];
169 | var data = localStorage.getItem(key);
170 | if(data != undefined){
171 | return callback(data);
172 | }else{
173 | $.get(path, function(d){
174 | localStorage.setItem(key, d);
175 | callback(d);
176 | });
177 | }
178 | }
179 |
180 |
181 |
182 | // 文件展示 ?a=view
183 | function file(path){
184 | var name = path.split('/').pop();
185 | var ext = name.split('.').pop().toLowerCase().replace(`?a=view`,"");
186 | if("|html|php|css|go|java|js|json|txt|sh|md|".indexOf(`|${ext}|`) >= 0){
187 | return file_code(path);
188 | }
189 |
190 | if("|mp4|webm|avi|".indexOf(`|${ext}|`) >= 0){
191 | return file_video(path);
192 | }
193 |
194 | if("|mpg|mpeg|mkv|rm|rmvb|mov|wmv|asf|ts|flv|".indexOf(`|${ext}|`) >= 0){
195 | return file_video(path);
196 | }
197 |
198 | if("|mp3|wav|ogg|m4a|".indexOf(`|${ext}|`) >= 0){
199 | return file_audio(path);
200 | }
201 |
202 | if("|bmp|jpg|jpeg|png|gif|".indexOf(`|${ext}|`) >= 0){
203 | return file_image(path);
204 | }
205 | }
206 |
207 | // 文件展示 |html|php|css|go|java|js|json|txt|sh|md|
208 | function file_code(path){
209 | var type = {
210 | "html":"html",
211 | "php":"php",
212 | "css":"css",
213 | "go":"golang",
214 | "java":"java",
215 | "js":"javascript",
216 | "json":"json",
217 | "txt":"Text",
218 | "sh":"sh",
219 | "md":"Markdown",
220 | };
221 | var name = path.split('/').pop();
222 | var ext = name.split('.').pop();
223 | var href = window.location.origin + path;
224 | var content = `
225 |
228 |
229 |
230 |
231 |
232 | file_download
233 |
234 |
235 |
236 | `;
237 | $('#content').html(content);
238 |
239 | $.get(path, function(data){
240 | $('#editor').html($('').text(data).html());
241 | var code_type = "Text";
242 | if(type[ext] != undefined ){
243 | code_type = type[ext];
244 | }
245 | var editor = ace.edit("editor");
246 | editor.setTheme("ace/theme/ambiance");
247 | editor.setFontSize(18);
248 | editor.session.setMode("ace/mode/"+code_type);
249 |
250 | //Autocompletion
251 | editor.setOptions({
252 | enableBasicAutocompletion: true,
253 | enableSnippets: true,
254 | enableLiveAutocompletion: true,
255 | maxLines: Infinity
256 | });
257 | });
258 | }
259 |
260 | // 文件展示 视频 |mp4|webm|avi|
261 | function file_video(path){
262 | var url = window.location.origin + path;
263 | var playBtn = `在 potplayer 中播放`;
264 | if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent)) { //移动端
265 | playBtn = ` 在mxplayer中播放`;
266 | }
267 | var content = `
268 |
269 |
270 |
273 |
${playBtn}
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 | file_download
285 | `;
286 | $('#content').html(content);
287 | }
288 |
289 | // 文件展示 音频 |mp3|m4a|wav|ogg|
290 | function file_audio(path){
291 | var url = window.location.origin + path;
292 | var content = `
293 |
294 |
295 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 | file_download
310 | `;
311 | $('#content').html(content);
312 | }
313 |
314 |
315 | // 图片展示
316 | function file_image(path){
317 | var url = window.location.origin + path;
318 | var content = `
319 |
320 |
321 |

322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 | file_download
338 | `;
339 | $('#content').html(content);
340 | }
341 |
342 |
343 | //时间转换
344 | function utc2beijing(utc_datetime) {
345 | // 转为正常的时间格式 年-月-日 时:分:秒
346 | var T_pos = utc_datetime.indexOf('T');
347 | var Z_pos = utc_datetime.indexOf('Z');
348 | var year_month_day = utc_datetime.substr(0,T_pos);
349 | var hour_minute_second = utc_datetime.substr(T_pos+1,Z_pos-T_pos-1);
350 | var new_datetime = year_month_day+" "+hour_minute_second; // 2017-03-31 08:02:06
351 |
352 | // 处理成为时间戳
353 | timestamp = new Date(Date.parse(new_datetime));
354 | timestamp = timestamp.getTime();
355 | timestamp = timestamp/1000;
356 |
357 | // 增加8个小时,北京时间比utc时间多八个时区
358 | var unixtimestamp = timestamp+8*60*60;
359 |
360 | // 时间戳转为时间
361 | var unixtimestamp = new Date(unixtimestamp*1000);
362 | var year = 1900 + unixtimestamp.getYear();
363 | var month = "0" + (unixtimestamp.getMonth() + 1);
364 | var date = "0" + unixtimestamp.getDate();
365 | var hour = "0" + unixtimestamp.getHours();
366 | var minute = "0" + unixtimestamp.getMinutes();
367 | var second = "0" + unixtimestamp.getSeconds();
368 | return year + "-" + month.substring(month.length-2, month.length) + "-" + date.substring(date.length-2, date.length)
369 | + " " + hour.substring(hour.length-2, hour.length) + ":"
370 | + minute.substring(minute.length-2, minute.length) + ":"
371 | + second.substring(second.length-2, second.length);
372 | }
373 |
374 | // bytes自适应转换到KB,MB,GB
375 | function formatFileSize(bytes) {
376 | if (bytes>=1000000000) {bytes=(bytes/1000000000).toFixed(2)+' GB';}
377 | else if (bytes>=1000000) {bytes=(bytes/1000000).toFixed(2)+' MB';}
378 | else if (bytes>=1000) {bytes=(bytes/1000).toFixed(2)+' KB';}
379 | else if (bytes>1) {bytes=bytes+' bytes';}
380 | else if (bytes==1) {bytes=bytes+' byte';}
381 | else {bytes='';}
382 | return bytes;
383 | }
384 |
385 | String.prototype.trim = function (char) {
386 | if (char) {
387 | return this.replace(new RegExp('^\\'+char+'+|\\'+char+'+$', 'g'), '');
388 | }
389 | return this.replace(/^\s+|\s+$/g, '');
390 | };
391 |
392 |
393 | // README.md HEAD.md 支持
394 | function markdown(el, data){
395 | if(window.md == undefined){
396 | //$.getScript('https://cdn.jsdelivr.net/npm/markdown-it@10.0.0/dist/markdown-it.min.js',function(){
397 | window.md = window.markdownit();
398 | markdown(el, data);
399 | //});
400 | }else{
401 | var html = md.render(data);
402 | $(el).show().html(html);
403 | }
404 | }
405 |
406 | // 监听回退事件
407 | window.onpopstate = function(){
408 | var path = window.location.pathname;
409 | render(path);
410 | }
411 |
412 |
413 | $(function(){
414 | init();
415 | var path = window.location.pathname;
416 | $("body").on("click",'.folder',function(){
417 | var url = $(this).attr('href');
418 | history.pushState(null, null, url);
419 | render(url);
420 | return false;
421 | });
422 |
423 | $("body").on("click",'.view',function(){
424 | var url = $(this).attr('href');
425 | history.pushState(null, null, url);
426 | render(url);
427 | return false;
428 | });
429 |
430 | render(path);
431 | });
432 |
--------------------------------------------------------------------------------
/使用及免责协议.md:
--------------------------------------------------------------------------------
1 | 用户须知:无论您是个人或组织、盈利与否、用途如何(包括以学习和研究为目的),均需仔细阅读本协议,包括免除goindex开发者责任的免责条款及对您的权利限制。请您审阅并接受或不接受本服务条款。如您不同意本服务条款及/或goindex开发者随时对其的修改,您应不使用或主动取消使用本程序。否则,您的任何对本程序的使用和修改等行为将被视为您对本服务条款全部的完全接受,包括接受goindex开发者对服务条款随时所做的任何修改。
2 |
3 | 在理解、同意、并遵守本协议的全部条款后,方可开始使用本软件。
4 |
5 | I. 前置条件
6 | 您应完全遵守中国大陆和您提供服务地区的相关法律法规,不得将使用本软件以任何形式用于任何违法用途。
7 |
8 | II. 协议许可的权利
9 |
10 | 您可以在完全遵守本许可协议的基础上,将本软件应用于非商业用途。
11 |
12 | 本软件及所附带的文件是作为不提供任何明确的或隐含的赔偿或担保的形式提供的。
13 |
14 | 用户出于自愿而使用本软件,您必须了解使用本软件的风险,goindex开发者不提供任何形式的技术支持、使用担保,也不承担任何因使用本软件而产生问题的相关责任。
15 |
16 | goindex开发者不对使用本软件构建的网站或者网站中的展示的文件及内容信息承担责任,,goindex开发者不承担任何直接、间接或者连带的责任,全部责任由您自行承担。
17 |
18 | goindex开发者无法全面监控您下载的程序完整性,因此不保证应用程序的合法性、安全性、完整性、真实性或品质等;您同意自行判断并承担所有风险。由此对您及第三人可能造成的损失,goindex开发者不承担任何直接、间接或者连带的责任。
19 |
20 | III. 代码修改和发布
21 |
22 | 基于本程序代码修改和发布的代码、软件、及构建的网站,与goindex开发者无关,其产生的责任和后果与goindex开发者无关,goindex开发者不承担任何责任。
23 |
24 | 一旦您开始安装、使用、修改goindex,即被视为完全理解并接受本协议的各项条款,在享有上述条款授予的权利的同时,受到相关的约束和限制。
25 |
--------------------------------------------------------------------------------