├── Cloudreve_FileSynchronizer.csproj
├── Cloudreve_FileSynchronizer.sln
├── EF
├── CloudreveContext.cs
├── Downloads.cs
├── Files.cs
├── Folders.cs
├── Groups.cs
├── Policies.cs
├── Settings.cs
├── Shares.cs
├── Tags.cs
├── Tasks.cs
├── Users.cs
└── Webdavs.cs
├── LICENSE
├── Program.cs
├── ProgramParameter.cs
├── Properties
└── launchSettings.json
└── README.md
/Cloudreve_FileSynchronizer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 | all
12 | runtime; build; native; contentfiles; analyzers; buildtransitive
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Cloudreve_FileSynchronizer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29806.167
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cloudreve_FileSynchronizer", "Cloudreve_FileSynchronizer.csproj", "{199BE4B1-CE9D-4616-9953-379CE99FCADC}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {199BE4B1-CE9D-4616-9953-379CE99FCADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {199BE4B1-CE9D-4616-9953-379CE99FCADC}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {199BE4B1-CE9D-4616-9953-379CE99FCADC}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {199BE4B1-CE9D-4616-9953-379CE99FCADC}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {8B752CE3-F0AC-42A6-B386-3D2B816B4D42}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/EF/CloudreveContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Metadata;
4 |
5 | namespace Cloudreve_FileSynchronizer.EF
6 | {
7 | public partial class CloudreveContext : DbContext
8 | {
9 | private string ConnectionString { get; set; }
10 |
11 | public CloudreveContext(string connectionString)
12 | {
13 | this.ConnectionString = connectionString;
14 | }
15 |
16 | public CloudreveContext(DbContextOptions options, string connectionString)
17 | : base(options)
18 | {
19 | this.ConnectionString = connectionString;
20 | }
21 |
22 | public virtual DbSet Downloads { get; set; }
23 | public virtual DbSet Files { get; set; }
24 | public virtual DbSet Folders { get; set; }
25 | public virtual DbSet Groups { get; set; }
26 | public virtual DbSet Policies { get; set; }
27 | public virtual DbSet Settings { get; set; }
28 | public virtual DbSet Shares { get; set; }
29 | public virtual DbSet Tags { get; set; }
30 | public virtual DbSet Tasks { get; set; }
31 | public virtual DbSet Users { get; set; }
32 | public virtual DbSet Webdavs { get; set; }
33 |
34 | protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
35 | {
36 | if (!optionsBuilder.IsConfigured)
37 | optionsBuilder.UseMySql(ConnectionString, x => x.ServerVersion("5.7.27-mysql"));
38 | }
39 |
40 | protected override void OnModelCreating(ModelBuilder modelBuilder)
41 | {
42 | modelBuilder.Entity(entity =>
43 | {
44 | entity.ToTable("downloads");
45 |
46 | entity.HasIndex(e => e.DeletedAt)
47 | .HasName("idx_downloads_deleted_at");
48 |
49 | entity.Property(e => e.Id)
50 | .HasColumnName("id")
51 | .HasColumnType("int(10) unsigned");
52 |
53 | entity.Property(e => e.Attrs)
54 | .HasColumnName("attrs")
55 | .HasColumnType("text")
56 | .HasCharSet("utf8mb4")
57 | .HasCollation("utf8mb4_bin");
58 |
59 | entity.Property(e => e.CreatedAt)
60 | .HasColumnName("created_at")
61 | .HasColumnType("timestamp");
62 |
63 | entity.Property(e => e.DeletedAt)
64 | .HasColumnName("deleted_at")
65 | .HasColumnType("timestamp");
66 |
67 | entity.Property(e => e.DownloadedSize)
68 | .HasColumnName("downloaded_size")
69 | .HasColumnType("bigint(20) unsigned");
70 |
71 | entity.Property(e => e.Dst)
72 | .HasColumnName("dst")
73 | .HasColumnType("text")
74 | .HasCharSet("utf8mb4")
75 | .HasCollation("utf8mb4_bin");
76 |
77 | entity.Property(e => e.Error)
78 | .HasColumnName("error")
79 | .HasColumnType("text")
80 | .HasCharSet("utf8mb4")
81 | .HasCollation("utf8mb4_bin");
82 |
83 | entity.Property(e => e.GId)
84 | .HasColumnName("g_id")
85 | .HasColumnType("longtext")
86 | .HasCharSet("utf8mb4")
87 | .HasCollation("utf8mb4_bin");
88 |
89 | entity.Property(e => e.Parent)
90 | .HasColumnName("parent")
91 | .HasColumnType("text")
92 | .HasCharSet("utf8mb4")
93 | .HasCollation("utf8mb4_bin");
94 |
95 | entity.Property(e => e.Source)
96 | .HasColumnName("source")
97 | .HasColumnType("text")
98 | .HasCharSet("utf8mb4")
99 | .HasCollation("utf8mb4_bin");
100 |
101 | entity.Property(e => e.Speed)
102 | .HasColumnName("speed")
103 | .HasColumnType("int(11)");
104 |
105 | entity.Property(e => e.Status)
106 | .HasColumnName("status")
107 | .HasColumnType("int(11)");
108 |
109 | entity.Property(e => e.TaskId)
110 | .HasColumnName("task_id")
111 | .HasColumnType("int(10) unsigned");
112 |
113 | entity.Property(e => e.TotalSize)
114 | .HasColumnName("total_size")
115 | .HasColumnType("bigint(20) unsigned");
116 |
117 | entity.Property(e => e.Type)
118 | .HasColumnName("type")
119 | .HasColumnType("int(11)");
120 |
121 | entity.Property(e => e.UpdatedAt)
122 | .HasColumnName("updated_at")
123 | .HasColumnType("timestamp");
124 |
125 | entity.Property(e => e.UserId)
126 | .HasColumnName("user_id")
127 | .HasColumnType("int(10) unsigned");
128 | });
129 |
130 | modelBuilder.Entity(entity =>
131 | {
132 | entity.ToTable("files");
133 |
134 | entity.HasIndex(e => e.DeletedAt)
135 | .HasName("idx_files_deleted_at");
136 |
137 | entity.HasIndex(e => e.FolderId)
138 | .HasName("folder_id");
139 |
140 | entity.HasIndex(e => e.UserId)
141 | .HasName("user_id");
142 |
143 | entity.HasIndex(e => new { e.Name, e.UserId, e.FolderId })
144 | .HasName("idx_only_one")
145 | .IsUnique();
146 |
147 | entity.Property(e => e.Id)
148 | .HasColumnName("id")
149 | .HasColumnType("int(10) unsigned");
150 |
151 | entity.Property(e => e.CreatedAt)
152 | .HasColumnName("created_at")
153 | .HasColumnType("timestamp");
154 |
155 | entity.Property(e => e.DeletedAt)
156 | .HasColumnName("deleted_at")
157 | .HasColumnType("timestamp");
158 |
159 | entity.Property(e => e.FolderId)
160 | .HasColumnName("folder_id")
161 | .HasColumnType("int(10) unsigned");
162 |
163 | entity.Property(e => e.Name)
164 | .HasColumnName("name")
165 | .HasColumnType("varchar(255)")
166 | .HasCharSet("utf8mb4")
167 | .HasCollation("utf8mb4_bin");
168 |
169 | entity.Property(e => e.PicInfo)
170 | .HasColumnName("pic_info")
171 | .HasColumnType("varchar(255)")
172 | .HasCharSet("utf8mb4")
173 | .HasCollation("utf8mb4_bin");
174 |
175 | entity.Property(e => e.PolicyId)
176 | .HasColumnName("policy_id")
177 | .HasColumnType("int(10) unsigned");
178 |
179 | entity.Property(e => e.Size)
180 | .HasColumnName("size")
181 | .HasColumnType("bigint(20) unsigned");
182 |
183 | entity.Property(e => e.SourceName)
184 | .HasColumnName("source_name")
185 | .HasColumnType("text")
186 | .HasCharSet("utf8mb4")
187 | .HasCollation("utf8mb4_bin");
188 |
189 | entity.Property(e => e.UpdatedAt)
190 | .HasColumnName("updated_at")
191 | .HasColumnType("timestamp");
192 |
193 | entity.Property(e => e.UserId)
194 | .HasColumnName("user_id")
195 | .HasColumnType("int(10) unsigned");
196 | });
197 |
198 | modelBuilder.Entity(entity =>
199 | {
200 | entity.ToTable("folders");
201 |
202 | entity.HasIndex(e => e.DeletedAt)
203 | .HasName("idx_folders_deleted_at");
204 |
205 | entity.HasIndex(e => e.OwnerId)
206 | .HasName("owner_id");
207 |
208 | entity.HasIndex(e => e.ParentId)
209 | .HasName("parent_id");
210 |
211 | entity.HasIndex(e => new { e.Name, e.ParentId })
212 | .HasName("idx_only_one_name")
213 | .IsUnique();
214 |
215 | entity.Property(e => e.Id)
216 | .HasColumnName("id")
217 | .HasColumnType("int(10) unsigned");
218 |
219 | entity.Property(e => e.CreatedAt)
220 | .HasColumnName("created_at")
221 | .HasColumnType("timestamp");
222 |
223 | entity.Property(e => e.DeletedAt)
224 | .HasColumnName("deleted_at")
225 | .HasColumnType("timestamp");
226 |
227 | entity.Property(e => e.Name)
228 | .HasColumnName("name")
229 | .HasColumnType("varchar(255)")
230 | .HasCharSet("utf8mb4")
231 | .HasCollation("utf8mb4_bin");
232 |
233 | entity.Property(e => e.OwnerId)
234 | .HasColumnName("owner_id")
235 | .HasColumnType("int(10) unsigned");
236 |
237 | entity.Property(e => e.ParentId)
238 | .HasColumnName("parent_id")
239 | .HasColumnType("int(10) unsigned");
240 |
241 | entity.Property(e => e.UpdatedAt)
242 | .HasColumnName("updated_at")
243 | .HasColumnType("timestamp");
244 | });
245 |
246 | modelBuilder.Entity(entity =>
247 | {
248 | entity.ToTable("groups");
249 |
250 | entity.HasIndex(e => e.DeletedAt)
251 | .HasName("idx_groups_deleted_at");
252 |
253 | entity.Property(e => e.Id)
254 | .HasColumnName("id")
255 | .HasColumnType("int(10) unsigned");
256 |
257 | entity.Property(e => e.CreatedAt)
258 | .HasColumnName("created_at")
259 | .HasColumnType("timestamp");
260 |
261 | entity.Property(e => e.DeletedAt)
262 | .HasColumnName("deleted_at")
263 | .HasColumnType("timestamp");
264 |
265 | entity.Property(e => e.MaxStorage)
266 | .HasColumnName("max_storage")
267 | .HasColumnType("bigint(20) unsigned");
268 |
269 | entity.Property(e => e.Name)
270 | .HasColumnName("name")
271 | .HasColumnType("varchar(255)")
272 | .HasCharSet("utf8mb4")
273 | .HasCollation("utf8mb4_bin");
274 |
275 | entity.Property(e => e.Options)
276 | .HasColumnName("options")
277 | .HasColumnType("varchar(255)")
278 | .HasCharSet("utf8mb4")
279 | .HasCollation("utf8mb4_bin");
280 |
281 | entity.Property(e => e.Policies)
282 | .HasColumnName("policies")
283 | .HasColumnType("varchar(255)")
284 | .HasCharSet("utf8mb4")
285 | .HasCollation("utf8mb4_bin");
286 |
287 | entity.Property(e => e.ShareEnabled).HasColumnName("share_enabled");
288 |
289 | entity.Property(e => e.SpeedLimit)
290 | .HasColumnName("speed_limit")
291 | .HasColumnType("int(11)");
292 |
293 | entity.Property(e => e.UpdatedAt)
294 | .HasColumnName("updated_at")
295 | .HasColumnType("timestamp");
296 |
297 | entity.Property(e => e.WebDavEnabled).HasColumnName("web_dav_enabled");
298 | });
299 |
300 | modelBuilder.Entity(entity =>
301 | {
302 | entity.ToTable("policies");
303 |
304 | entity.HasIndex(e => e.DeletedAt)
305 | .HasName("idx_policies_deleted_at");
306 |
307 | entity.Property(e => e.Id)
308 | .HasColumnName("id")
309 | .HasColumnType("int(10) unsigned");
310 |
311 | entity.Property(e => e.AccessKey)
312 | .HasColumnName("access_key")
313 | .HasColumnType("text")
314 | .HasCharSet("utf8mb4")
315 | .HasCollation("utf8mb4_bin");
316 |
317 | entity.Property(e => e.AutoRename).HasColumnName("auto_rename");
318 |
319 | entity.Property(e => e.BaseUrl)
320 | .HasColumnName("base_url")
321 | .HasColumnType("varchar(255)")
322 | .HasCharSet("utf8mb4")
323 | .HasCollation("utf8mb4_bin");
324 |
325 | entity.Property(e => e.BucketName)
326 | .HasColumnName("bucket_name")
327 | .HasColumnType("varchar(255)")
328 | .HasCharSet("utf8mb4")
329 | .HasCollation("utf8mb4_bin");
330 |
331 | entity.Property(e => e.CreatedAt)
332 | .HasColumnName("created_at")
333 | .HasColumnType("timestamp");
334 |
335 | entity.Property(e => e.DeletedAt)
336 | .HasColumnName("deleted_at")
337 | .HasColumnType("timestamp");
338 |
339 | entity.Property(e => e.DirNameRule)
340 | .HasColumnName("dir_name_rule")
341 | .HasColumnType("varchar(255)")
342 | .HasCharSet("utf8mb4")
343 | .HasCollation("utf8mb4_bin");
344 |
345 | entity.Property(e => e.FileNameRule)
346 | .HasColumnName("file_name_rule")
347 | .HasColumnType("varchar(255)")
348 | .HasCharSet("utf8mb4")
349 | .HasCollation("utf8mb4_bin");
350 |
351 | entity.Property(e => e.IsOriginLinkEnable).HasColumnName("is_origin_link_enable");
352 |
353 | entity.Property(e => e.IsPrivate).HasColumnName("is_private");
354 |
355 | entity.Property(e => e.MaxSize)
356 | .HasColumnName("max_size")
357 | .HasColumnType("bigint(20) unsigned");
358 |
359 | entity.Property(e => e.Name)
360 | .HasColumnName("name")
361 | .HasColumnType("varchar(255)")
362 | .HasCharSet("utf8mb4")
363 | .HasCollation("utf8mb4_bin");
364 |
365 | entity.Property(e => e.Options)
366 | .HasColumnName("options")
367 | .HasColumnType("text")
368 | .HasCharSet("utf8mb4")
369 | .HasCollation("utf8mb4_bin");
370 |
371 | entity.Property(e => e.SecretKey)
372 | .HasColumnName("secret_key")
373 | .HasColumnType("text")
374 | .HasCharSet("utf8mb4")
375 | .HasCollation("utf8mb4_bin");
376 |
377 | entity.Property(e => e.Server)
378 | .HasColumnName("server")
379 | .HasColumnType("varchar(255)")
380 | .HasCharSet("utf8mb4")
381 | .HasCollation("utf8mb4_bin");
382 |
383 | entity.Property(e => e.Type)
384 | .HasColumnName("type")
385 | .HasColumnType("varchar(255)")
386 | .HasCharSet("utf8mb4")
387 | .HasCollation("utf8mb4_bin");
388 |
389 | entity.Property(e => e.UpdatedAt)
390 | .HasColumnName("updated_at")
391 | .HasColumnType("timestamp");
392 | });
393 |
394 | modelBuilder.Entity(entity =>
395 | {
396 | entity.ToTable("settings");
397 |
398 | entity.HasIndex(e => e.DeletedAt)
399 | .HasName("idx_settings_deleted_at");
400 |
401 | entity.HasIndex(e => e.Name)
402 | .HasName("setting_key");
403 |
404 | entity.Property(e => e.Id)
405 | .HasColumnName("id")
406 | .HasColumnType("int(10) unsigned");
407 |
408 | entity.Property(e => e.CreatedAt)
409 | .HasColumnName("created_at")
410 | .HasColumnType("timestamp");
411 |
412 | entity.Property(e => e.DeletedAt)
413 | .HasColumnName("deleted_at")
414 | .HasColumnType("timestamp");
415 |
416 | entity.Property(e => e.Name)
417 | .IsRequired()
418 | .HasColumnName("name")
419 | .HasColumnType("varchar(255)")
420 | .HasCharSet("utf8mb4")
421 | .HasCollation("utf8mb4_bin");
422 |
423 | entity.Property(e => e.Type)
424 | .IsRequired()
425 | .HasColumnName("type")
426 | .HasColumnType("varchar(255)")
427 | .HasCharSet("utf8mb4")
428 | .HasCollation("utf8mb4_bin");
429 |
430 | entity.Property(e => e.UpdatedAt)
431 | .HasColumnName("updated_at")
432 | .HasColumnType("timestamp");
433 |
434 | entity.Property(e => e.Value)
435 | .HasColumnName("value")
436 | .HasColumnType("longtext")
437 | .HasCharSet("utf8mb4")
438 | .HasCollation("utf8mb4_bin");
439 | });
440 |
441 | modelBuilder.Entity(entity =>
442 | {
443 | entity.ToTable("shares");
444 |
445 | entity.HasIndex(e => e.DeletedAt)
446 | .HasName("idx_shares_deleted_at");
447 |
448 | entity.HasIndex(e => e.SourceName)
449 | .HasName("source");
450 |
451 | entity.Property(e => e.Id)
452 | .HasColumnName("id")
453 | .HasColumnType("int(10) unsigned");
454 |
455 | entity.Property(e => e.CreatedAt)
456 | .HasColumnName("created_at")
457 | .HasColumnType("timestamp");
458 |
459 | entity.Property(e => e.DeletedAt)
460 | .HasColumnName("deleted_at")
461 | .HasColumnType("timestamp");
462 |
463 | entity.Property(e => e.Downloads)
464 | .HasColumnName("downloads")
465 | .HasColumnType("int(11)");
466 |
467 | entity.Property(e => e.Expires)
468 | .HasColumnName("expires")
469 | .HasColumnType("timestamp");
470 |
471 | entity.Property(e => e.IsDir).HasColumnName("is_dir");
472 |
473 | entity.Property(e => e.Password)
474 | .HasColumnName("password")
475 | .HasColumnType("varchar(255)")
476 | .HasCharSet("utf8mb4")
477 | .HasCollation("utf8mb4_bin");
478 |
479 | entity.Property(e => e.PreviewEnabled).HasColumnName("preview_enabled");
480 |
481 | entity.Property(e => e.RemainDownloads)
482 | .HasColumnName("remain_downloads")
483 | .HasColumnType("int(11)");
484 |
485 | entity.Property(e => e.SourceId)
486 | .HasColumnName("source_id")
487 | .HasColumnType("int(10) unsigned");
488 |
489 | entity.Property(e => e.SourceName)
490 | .HasColumnName("source_name")
491 | .HasColumnType("varchar(255)")
492 | .HasCharSet("utf8mb4")
493 | .HasCollation("utf8mb4_bin");
494 |
495 | entity.Property(e => e.UpdatedAt)
496 | .HasColumnName("updated_at")
497 | .HasColumnType("timestamp");
498 |
499 | entity.Property(e => e.UserId)
500 | .HasColumnName("user_id")
501 | .HasColumnType("int(10) unsigned");
502 |
503 | entity.Property(e => e.Views)
504 | .HasColumnName("views")
505 | .HasColumnType("int(11)");
506 | });
507 |
508 | modelBuilder.Entity(entity =>
509 | {
510 | entity.ToTable("tags");
511 |
512 | entity.HasIndex(e => e.DeletedAt)
513 | .HasName("idx_tags_deleted_at");
514 |
515 | entity.Property(e => e.Id)
516 | .HasColumnName("id")
517 | .HasColumnType("int(10) unsigned");
518 |
519 | entity.Property(e => e.Color)
520 | .HasColumnName("color")
521 | .HasColumnType("varchar(255)")
522 | .HasCharSet("utf8mb4")
523 | .HasCollation("utf8mb4_bin");
524 |
525 | entity.Property(e => e.CreatedAt)
526 | .HasColumnName("created_at")
527 | .HasColumnType("timestamp");
528 |
529 | entity.Property(e => e.DeletedAt)
530 | .HasColumnName("deleted_at")
531 | .HasColumnType("timestamp");
532 |
533 | entity.Property(e => e.Expression)
534 | .HasColumnName("expression")
535 | .HasColumnType("text")
536 | .HasCharSet("utf8mb4")
537 | .HasCollation("utf8mb4_bin");
538 |
539 | entity.Property(e => e.Icon)
540 | .HasColumnName("icon")
541 | .HasColumnType("varchar(255)")
542 | .HasCharSet("utf8mb4")
543 | .HasCollation("utf8mb4_bin");
544 |
545 | entity.Property(e => e.Name)
546 | .HasColumnName("name")
547 | .HasColumnType("varchar(255)")
548 | .HasCharSet("utf8mb4")
549 | .HasCollation("utf8mb4_bin");
550 |
551 | entity.Property(e => e.Type)
552 | .HasColumnName("type")
553 | .HasColumnType("int(11)");
554 |
555 | entity.Property(e => e.UpdatedAt)
556 | .HasColumnName("updated_at")
557 | .HasColumnType("timestamp");
558 |
559 | entity.Property(e => e.UserId)
560 | .HasColumnName("user_id")
561 | .HasColumnType("int(10) unsigned");
562 | });
563 |
564 | modelBuilder.Entity(entity =>
565 | {
566 | entity.ToTable("tasks");
567 |
568 | entity.HasIndex(e => e.DeletedAt)
569 | .HasName("idx_tasks_deleted_at");
570 |
571 | entity.Property(e => e.Id)
572 | .HasColumnName("id")
573 | .HasColumnType("int(10) unsigned");
574 |
575 | entity.Property(e => e.CreatedAt)
576 | .HasColumnName("created_at")
577 | .HasColumnType("timestamp");
578 |
579 | entity.Property(e => e.DeletedAt)
580 | .HasColumnName("deleted_at")
581 | .HasColumnType("timestamp");
582 |
583 | entity.Property(e => e.Error)
584 | .HasColumnName("error")
585 | .HasColumnType("text")
586 | .HasCharSet("utf8mb4")
587 | .HasCollation("utf8mb4_bin");
588 |
589 | entity.Property(e => e.Progress)
590 | .HasColumnName("progress")
591 | .HasColumnType("int(11)");
592 |
593 | entity.Property(e => e.Props)
594 | .HasColumnName("props")
595 | .HasColumnType("text")
596 | .HasCharSet("utf8mb4")
597 | .HasCollation("utf8mb4_bin");
598 |
599 | entity.Property(e => e.Status)
600 | .HasColumnName("status")
601 | .HasColumnType("int(11)");
602 |
603 | entity.Property(e => e.Type)
604 | .HasColumnName("type")
605 | .HasColumnType("int(11)");
606 |
607 | entity.Property(e => e.UpdatedAt)
608 | .HasColumnName("updated_at")
609 | .HasColumnType("timestamp");
610 |
611 | entity.Property(e => e.UserId)
612 | .HasColumnName("user_id")
613 | .HasColumnType("int(10) unsigned");
614 | });
615 |
616 | modelBuilder.Entity(entity =>
617 | {
618 | entity.ToTable("users");
619 |
620 | entity.HasIndex(e => e.DeletedAt)
621 | .HasName("idx_users_deleted_at");
622 |
623 | entity.HasIndex(e => e.Email)
624 | .HasName("uix_users_email")
625 | .IsUnique();
626 |
627 | entity.Property(e => e.Id)
628 | .HasColumnName("id")
629 | .HasColumnType("int(10) unsigned");
630 |
631 | entity.Property(e => e.Authn)
632 | .HasColumnName("authn")
633 | .HasColumnType("text")
634 | .HasCharSet("utf8mb4")
635 | .HasCollation("utf8mb4_bin");
636 |
637 | entity.Property(e => e.Avatar)
638 | .HasColumnName("avatar")
639 | .HasColumnType("varchar(255)")
640 | .HasCharSet("utf8mb4")
641 | .HasCollation("utf8mb4_bin");
642 |
643 | entity.Property(e => e.CreatedAt)
644 | .HasColumnName("created_at")
645 | .HasColumnType("timestamp");
646 |
647 | entity.Property(e => e.DeletedAt)
648 | .HasColumnName("deleted_at")
649 | .HasColumnType("timestamp");
650 |
651 | entity.Property(e => e.Email)
652 | .HasColumnName("email")
653 | .HasColumnType("varchar(100)")
654 | .HasCharSet("utf8mb4")
655 | .HasCollation("utf8mb4_bin");
656 |
657 | entity.Property(e => e.GroupId)
658 | .HasColumnName("group_id")
659 | .HasColumnType("int(10) unsigned");
660 |
661 | entity.Property(e => e.Nick)
662 | .HasColumnName("nick")
663 | .HasColumnType("varchar(50)")
664 | .HasCharSet("utf8mb4")
665 | .HasCollation("utf8mb4_bin");
666 |
667 | entity.Property(e => e.Options)
668 | .HasColumnName("options")
669 | .HasColumnType("varchar(255)")
670 | .HasCharSet("utf8mb4")
671 | .HasCollation("utf8mb4_bin");
672 |
673 | entity.Property(e => e.Password)
674 | .HasColumnName("password")
675 | .HasColumnType("varchar(255)")
676 | .HasCharSet("utf8mb4")
677 | .HasCollation("utf8mb4_bin");
678 |
679 | entity.Property(e => e.Status)
680 | .HasColumnName("status")
681 | .HasColumnType("int(11)");
682 |
683 | entity.Property(e => e.Storage)
684 | .HasColumnName("storage")
685 | .HasColumnType("bigint(20) unsigned");
686 |
687 | entity.Property(e => e.TwoFactor)
688 | .HasColumnName("two_factor")
689 | .HasColumnType("varchar(255)")
690 | .HasCharSet("utf8mb4")
691 | .HasCollation("utf8mb4_bin");
692 |
693 | entity.Property(e => e.UpdatedAt)
694 | .HasColumnName("updated_at")
695 | .HasColumnType("timestamp");
696 | });
697 |
698 | modelBuilder.Entity(entity =>
699 | {
700 | entity.ToTable("webdavs");
701 |
702 | entity.HasIndex(e => e.DeletedAt)
703 | .HasName("idx_webdavs_deleted_at");
704 |
705 | entity.HasIndex(e => new { e.Password, e.UserId })
706 | .HasName("password_only_on")
707 | .IsUnique();
708 |
709 | entity.Property(e => e.Id)
710 | .HasColumnName("id")
711 | .HasColumnType("int(10) unsigned");
712 |
713 | entity.Property(e => e.CreatedAt)
714 | .HasColumnName("created_at")
715 | .HasColumnType("timestamp");
716 |
717 | entity.Property(e => e.DeletedAt)
718 | .HasColumnName("deleted_at")
719 | .HasColumnType("timestamp");
720 |
721 | entity.Property(e => e.Name)
722 | .HasColumnName("name")
723 | .HasColumnType("varchar(255)")
724 | .HasCharSet("utf8mb4")
725 | .HasCollation("utf8mb4_bin");
726 |
727 | entity.Property(e => e.Password)
728 | .HasColumnName("password")
729 | .HasColumnType("varchar(255)")
730 | .HasCharSet("utf8mb4")
731 | .HasCollation("utf8mb4_bin");
732 |
733 | entity.Property(e => e.Root)
734 | .HasColumnName("root")
735 | .HasColumnType("text")
736 | .HasCharSet("utf8mb4")
737 | .HasCollation("utf8mb4_bin");
738 |
739 | entity.Property(e => e.UpdatedAt)
740 | .HasColumnName("updated_at")
741 | .HasColumnType("timestamp");
742 |
743 | entity.Property(e => e.UserId)
744 | .HasColumnName("user_id")
745 | .HasColumnType("int(10) unsigned");
746 | });
747 |
748 | OnModelCreatingPartial(modelBuilder);
749 | }
750 |
751 | partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
752 | }
753 | }
754 |
--------------------------------------------------------------------------------
/EF/Downloads.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Downloads
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public int? Status { get; set; }
13 | public int? Type { get; set; }
14 | public string Source { get; set; }
15 | public ulong? TotalSize { get; set; }
16 | public ulong? DownloadedSize { get; set; }
17 | public string GId { get; set; }
18 | public int? Speed { get; set; }
19 | public string Parent { get; set; }
20 | public string Attrs { get; set; }
21 | public string Error { get; set; }
22 | public string Dst { get; set; }
23 | public uint? UserId { get; set; }
24 | public uint? TaskId { get; set; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EF/Files.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Files
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Name { get; set; }
13 | public string SourceName { get; set; }
14 | public uint? UserId { get; set; }
15 | public ulong? Size { get; set; }
16 | public string PicInfo { get; set; }
17 | public uint? FolderId { get; set; }
18 | public uint? PolicyId { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EF/Folders.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Folders
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Name { get; set; }
13 | public uint? ParentId { get; set; }
14 | public uint? OwnerId { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/EF/Groups.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Groups
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Name { get; set; }
13 | public string Policies { get; set; }
14 | public ulong? MaxStorage { get; set; }
15 | public bool? ShareEnabled { get; set; }
16 | public bool? WebDavEnabled { get; set; }
17 | public int? SpeedLimit { get; set; }
18 | public string Options { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/EF/Policies.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Policies
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Name { get; set; }
13 | public string Type { get; set; }
14 | public string Server { get; set; }
15 | public string BucketName { get; set; }
16 | public bool? IsPrivate { get; set; }
17 | public string BaseUrl { get; set; }
18 | public string AccessKey { get; set; }
19 | public string SecretKey { get; set; }
20 | public ulong? MaxSize { get; set; }
21 | public bool? AutoRename { get; set; }
22 | public string DirNameRule { get; set; }
23 | public string FileNameRule { get; set; }
24 | public bool? IsOriginLinkEnable { get; set; }
25 | public string Options { get; set; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/EF/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Settings
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Type { get; set; }
13 | public string Name { get; set; }
14 | public string Value { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/EF/Shares.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Shares
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Password { get; set; }
13 | public bool? IsDir { get; set; }
14 | public uint? UserId { get; set; }
15 | public uint? SourceId { get; set; }
16 | public int? Views { get; set; }
17 | public int? Downloads { get; set; }
18 | public int? RemainDownloads { get; set; }
19 | public DateTime? Expires { get; set; }
20 | public bool? PreviewEnabled { get; set; }
21 | public string SourceName { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EF/Tags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Tags
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Name { get; set; }
13 | public string Icon { get; set; }
14 | public string Color { get; set; }
15 | public int? Type { get; set; }
16 | public string Expression { get; set; }
17 | public uint? UserId { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/EF/Tasks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Tasks
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public int? Status { get; set; }
13 | public int? Type { get; set; }
14 | public uint? UserId { get; set; }
15 | public int? Progress { get; set; }
16 | public string Error { get; set; }
17 | public string Props { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/EF/Users.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Users
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Email { get; set; }
13 | public string Nick { get; set; }
14 | public string Password { get; set; }
15 | public int? Status { get; set; }
16 | public uint? GroupId { get; set; }
17 | public ulong? Storage { get; set; }
18 | public string TwoFactor { get; set; }
19 | public string Avatar { get; set; }
20 | public string Options { get; set; }
21 | public string Authn { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EF/Webdavs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Cloudreve_FileSynchronizer.EF
5 | {
6 | public partial class Webdavs
7 | {
8 | public uint Id { get; set; }
9 | public DateTime? CreatedAt { get; set; }
10 | public DateTime? UpdatedAt { get; set; }
11 | public DateTime? DeletedAt { get; set; }
12 | public string Name { get; set; }
13 | public string Password { get; set; }
14 | public uint? UserId { get; set; }
15 | public string Root { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using Cloudreve_FileSynchronizer.EF;
2 | using Mono.Options;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Drawing;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Threading.Tasks;
9 |
10 | namespace Cloudreve_FileSynchronizer
11 | {
12 | class Program
13 | {
14 | private const string IMAGE_EXT = ".jpg|.jpeg|.gif|.tiff|.png|.svg";
15 |
16 | static void Main(string[] args)
17 | {
18 | ProgramParameter parameter = ParseParameter(args);
19 |
20 | if (parameter == null)
21 | return;
22 |
23 | if(!Directory.Exists(parameter.SourceDirectory))
24 | {
25 | Console.WriteLine("Error: source directory does not exist");
26 | return;
27 | }
28 |
29 | DirectoryInfo srcDir = new DirectoryInfo(parameter.SourceDirectory);
30 |
31 | Task.WaitAll(Task.Run(async () =>
32 | {
33 | using (var dbContext = new CloudreveContext(parameter.ConnectionString))
34 | {
35 | var user = dbContext.Users.FirstOrDefault(p =>
36 | p.Email == parameter.UserName || p.Nick == parameter.UserName);
37 |
38 | if (user == null)
39 | {
40 | Console.WriteLine($"Error: user \"{parameter.UserName}\" does not exist");
41 | return;
42 | }
43 |
44 | var policy = dbContext.Policies.FirstOrDefault(p => p.Id == parameter.StoragePolicy);
45 |
46 | if (policy == null)
47 | {
48 | Console.WriteLine($"Error: Storage policy (ID = \"{parameter.StoragePolicy}\") does not exist");
49 | return;
50 | }
51 |
52 | Folders rootFolder = FindRootFolder(parameter.Target, dbContext);
53 |
54 | if (rootFolder == null)
55 | {
56 | Console.WriteLine($"target path \"{parameter.Target}\" does not exist in Cloudreve's database");
57 | return;
58 | }
59 | else
60 | {
61 | Console.WriteLine($"target path \"{parameter.Target}\" found. ID = {rootFolder.Id}");
62 | Console.WriteLine($"Synchronizing files and directories");
63 | }
64 |
65 | var trans = await dbContext.Database.BeginTransactionAsync();
66 |
67 | try
68 | {
69 | await SaveTree(srcDir, rootFolder, parameter.Target, user, policy, dbContext);
70 |
71 | await dbContext.SaveChangesAsync();
72 |
73 | await trans.CommitAsync();
74 |
75 | Console.WriteLine($"Transaction committed.");
76 | Console.WriteLine($"Successed.");
77 | }
78 | catch(Exception ex)
79 | {
80 | await trans.RollbackAsync();
81 |
82 | Console.WriteLine($"An has error occured durning synchronization: {ex.Message}{(ex.InnerException == null ? "" : $" ({ex.InnerException.Message})")}");
83 | Console.WriteLine("Stack trace:");
84 | Console.WriteLine(ex.StackTrace);
85 | }
86 | finally
87 | {
88 | trans.Dispose();
89 | }
90 |
91 | }
92 | }));
93 |
94 | }
95 |
96 |
97 | public static async Task SaveTree(DirectoryInfo dir, Folders parentDir,
98 | string thisDirPath, Users user, Policies policy, CloudreveContext dbContext)
99 | {
100 | string dirName = dir.Name;
101 |
102 | Console.WriteLine($" - Processing directory \"{thisDirPath}\"");
103 |
104 | var newFolder = dbContext.Folders.Add(new Folders()
105 | {
106 | OwnerId = user.Id,
107 | Name = dirName,
108 | ParentId = parentDir.Id,
109 | CreatedAt = DateTime.Now,
110 | UpdatedAt = DateTime.Now
111 | });
112 |
113 | await dbContext.SaveChangesAsync();
114 |
115 | Console.WriteLine($" - Folder \"{thisDirPath}\" created. ID = {newFolder.Entity.Id}");
116 |
117 | var files =
118 | dir.GetFiles().Select(p =>
119 | {
120 | string picinfo = null, fileFullPath;
121 |
122 | if (!String.IsNullOrEmpty(p.Extension) && IMAGE_EXT.Contains(p.Extension.ToLower()))
123 | picinfo = GetImageSizeStr(p);
124 |
125 | fileFullPath = (thisDirPath + "/" + p.Name);
126 |
127 | var file = new Files()
128 | {
129 | FolderId = newFolder.Entity.Id,
130 | Name = p.Name,
131 | PicInfo = picinfo,
132 | PolicyId = policy.Id,
133 | Size = (ulong)p.Length,
134 | SourceName = fileFullPath,
135 | UserId = user.Id,
136 | CreatedAt = DateTime.Now,
137 | UpdatedAt = DateTime.Now
138 | };
139 |
140 | Console.WriteLine($" - File \"{fileFullPath}\" created.");
141 |
142 | return file;
143 | });
144 |
145 | await dbContext.Files.AddRangeAsync(files);
146 |
147 | Console.WriteLine($" - {files.Count()} files created.");
148 |
149 | var subDirectories = dir.GetDirectories();
150 | if (subDirectories.Length > 0)
151 | {
152 | Console.WriteLine($" - {subDirectories.Length} sub directories found. Processing sub directories...");
153 |
154 | foreach (var subDir in subDirectories)
155 | await SaveTree(subDir, newFolder.Entity, (thisDirPath + "/" + subDir.Name), user, policy, dbContext);
156 | }
157 | else
158 | {
159 | Console.WriteLine($" - Leaf directory, leaving...");
160 | }
161 |
162 | }
163 |
164 | private static string GetImageSizeStr(FileInfo p)
165 | {
166 | string picinfo = null;
167 | FileStream imageFileStream = null;
168 | Image image = null;
169 | try
170 | {
171 | imageFileStream = p.Open(FileMode.Open);
172 |
173 | image = Image.FromStream(imageFileStream, false, false);
174 |
175 | picinfo = $"{image.Width},{image.Height}";
176 |
177 | }
178 | catch(Exception ex)
179 | {
180 | Console.WriteLine($" - \"{p.Name}\" Failed to get image dimensions: {ex.Message}");
181 | }
182 | finally
183 | {
184 | if (imageFileStream != null)
185 | imageFileStream.Dispose();
186 |
187 | if (image != null)
188 | image.Dispose();
189 | }
190 |
191 | return picinfo;
192 | }
193 |
194 | private static Folders FindRootFolder(string targetPath, CloudreveContext dbContext)
195 | {
196 | Folders rootFolder = null;
197 |
198 | if (targetPath == "/")
199 | {
200 | rootFolder = dbContext.Folders.FirstOrDefault(p => p.Name == "/");
201 | }
202 | else
203 | {
204 |
205 | var pathParts = targetPath.Split('/');
206 |
207 | if (String.IsNullOrWhiteSpace(pathParts.Last()))
208 | pathParts = pathParts.Take(pathParts.Length - 1).ToArray();
209 |
210 | var beginning = pathParts.Last();
211 |
212 | var folderRecord = dbContext.Folders.Where(p => p.Name == beginning).ToArray();
213 |
214 | foreach (var rec in folderRecord)
215 | {
216 | int i = pathParts.Length - 1;
217 | var cur = rec;
218 |
219 | do i--;while (i > 0 && (cur = dbContext.Folders.FirstOrDefault(p => p.Id == cur.ParentId))?.Name == pathParts[i]);
220 |
221 | if (i == 0)
222 | {
223 | rootFolder = rec;
224 | break;
225 | }
226 | }
227 | }
228 |
229 | return rootFolder;
230 | }
231 |
232 | private static ProgramParameter ParseParameter(string[] args)
233 | {
234 | ProgramParameter programParameter = new ProgramParameter();
235 | bool success = true;
236 |
237 | //string connectionString = "Server={SERVER};Port={PORT};database={DATABASE};uid={UID};pwd={PWD};SslMode=None";
238 | var opt = new OptionSet() {
239 | {
240 | "s|source-dir=", "source directory to upload",
241 | v => {
242 |
243 | if(String.IsNullOrEmpty(v))
244 | {
245 | Console.WriteLine("source directory cannot be empty");
246 | success = false;
247 | }
248 | else
249 | programParameter.SourceDirectory = v;
250 | }
251 | },
252 | {
253 | "n|user-name=", "upload for whom",
254 | v =>
255 | {
256 | if(String.IsNullOrEmpty(v))
257 | {
258 | Console.WriteLine("user name cannot be empty");
259 | success = false;
260 | }
261 | else
262 | programParameter.UserName = v;
263 | }
264 | },
265 | {
266 | "p|policy-id=", "storage policy id",
267 | v =>
268 | {
269 | if(String.IsNullOrEmpty(v))
270 | {
271 | Console.WriteLine("storage policy id cannot be empty");
272 | success = false;
273 | }
274 |
275 | programParameter.StoragePolicy = Int32.Parse(v);
276 | }
277 | },
278 | {
279 | "t|target=", "base directory path",
280 | v =>
281 | {
282 | if(String.IsNullOrEmpty(v))
283 | {
284 | Console.WriteLine("target directory cannot be empty");
285 | success = false;
286 | }
287 | else if (!v.StartsWith("/"))
288 | {
289 | Console.WriteLine("target must start with \"/\"");
290 | success = false;
291 | }
292 |
293 | else
294 | programParameter.Target = v;
295 | }
296 | },
297 | {
298 | "c|connection-string=", "Connection string to a mysql instance",
299 | v =>
300 | {
301 | if(String.IsNullOrEmpty(v))
302 | {
303 | Console.WriteLine("connection string cannot be empty");
304 | success = false;
305 | }
306 | else
307 | {
308 | programParameter.ConnectionString = v;
309 | }
310 |
311 |
312 | }
313 | },
314 | {
315 | "h|help", "show help",
316 | v =>
317 | {
318 | //do nothing
319 | }
320 | }
321 | };
322 |
323 | if (args == null || args.Length == 0 || args.Any(p => p == "-h" || p == "--help"))
324 | {
325 | ShowHelp(opt);
326 | return null;
327 | }
328 |
329 | List extra;
330 | try
331 | {
332 | extra = opt.Parse(args);
333 |
334 | if (!success)
335 | return null;
336 |
337 | return programParameter;
338 | }
339 | catch (OptionException e)
340 | {
341 | Console.Write("invalid parameter: ");
342 | Console.WriteLine(e.Message);
343 | Console.WriteLine("Try `--help' for more information.");
344 | return null;
345 | }
346 | }
347 |
348 | private static void ShowHelp(OptionSet p)
349 | {
350 | Console.WriteLine("This tool is used to post-record data for files that not uploaded by Cloudreve but uploaded via ftp or disk copy.");
351 | Console.WriteLine("This won't perform a real file copy but just make files under control in Cloudreve.");
352 | Console.WriteLine("this tool only work for Cloudreve version 3.0+ (using MySQL database)");
353 | Console.WriteLine();
354 | Console.WriteLine("Options:");
355 | p.WriteOptionDescriptions(Console.Out);
356 | }
357 | }
358 | }
359 |
--------------------------------------------------------------------------------
/ProgramParameter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace Cloudreve_FileSynchronizer
6 | {
7 | public class ProgramParameter
8 | {
9 | private string sourceDirectory;
10 | private string target;
11 | private string userName;
12 | private string connectionString;
13 | private bool showHelp;
14 | private int storagePolicy;
15 |
16 | public string SourceDirectory { get => sourceDirectory; set => sourceDirectory = value?.Trim(); }
17 | public string Target { get => target; set => target = value?.Trim(); }
18 | public string UserName { get => userName; set => userName = value?.Trim(); }
19 | public string ConnectionString { get => connectionString; set => connectionString = value?.Trim(); }
20 | public bool ShowHelp { get => showHelp; set => showHelp = value; }
21 | public int StoragePolicy { get => storagePolicy; set => storagePolicy = value; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Cloudreve_FileSynchronizer": {
4 | "commandName": "Project",
5 | "commandLineArgs": ""
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cloudreve File Synchronizer
2 |
3 | A simple post-offline-copy file list synchronizer.
4 |
5 | - This tool is used to post-record data for files that not uploaded by Cloudreve but uploaded via ftp or disk copy.
6 | - This tool won't perform a real file copy but just make files under control in Cloudreve.
7 | - Only work for Cloudreve version 3.0+ (using MySQL database)
8 |
9 |
10 |
11 | # Usage:
12 |
13 | ```
14 | Cloudreve_FileSynchronizer -s /source/directory -t /target/path/in/cloudreve -n user-name -p policy-id -c "Server=xxx;Port=3306;database=Cloudreve_Database;uid=xxxx;pwd=xxx;"
15 | ```
16 |
17 | Options:
18 |
19 | -s, --source-dir=VALUE source directory to upload
20 |
21 | -n, --user-name=VALUE upload for whom
22 |
23 | -p, --policy-id=VALUE storage policy id
24 |
25 | -t, --target=VALUE base directory path
26 |
27 | -c, --connection-string=VALUE Connection string to a mysql instance
28 |
29 | -h, --help show help
30 |
31 | # Requirements
32 | .NET Core 3.1
33 |
34 | libgdiplus (Linux)
35 |
--------------------------------------------------------------------------------