Archived
Merge pull request #541 from alex2108/master
This commit is contained in:
commit
586d49f0c3
5 files changed
+395
-12
No files matched your search
+2
-2
@@ -28,7 +28,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["angular.min.js"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7d7b73db4692f8fffa1413febc016953a06c6753fbb34ce712d9ded3c6b15d969dbd2a457b059243111108307848e6d9faeed7dd3300e60942b292ddad3a566249404f4f4f4f4fbfa6673899b0a36cb3cde3f355c9864723f6e8e0e137ec6fd14536633f64f9398bd20540a4651ecfaa32cb0b362c3867e58ab3a337afdfbf3bfee1c3fb37ef4ed8324ef828dc0374df2709237405cb79c1f34bbe08d98782b36c09cde282155995cf399b670bcee0cff3ec92e7295fb0d9163a633f1dbfdf2fca6dc2115712cf795a607751c9e6f076c6d932ab80a438251a5e1d1fbd787df282ba0ff7f626f77f2d92382dd92ccfaea0ef27accc2b3e86bed2324e2b5effbd49aa02ff177fb3fb1368799e64b32861f79eb06594140014a5e75512e5f26f04da0b2af8a5005ecccbe0706fef32ca59b14de730acf49c4deb16e13a5b54091f06cdbb60cc4e834d54cca36493f3f9aa0ccb3c4a8b242a7970363a2444559ecc22403f6501f08df037ed4318c0323e1f2e2b781067291bde5b95e5e66d9e5dc60b9e8fd9bd065ffd6cc43eed31f86880e1822fa32a298bf063912fff9347f0ec75b4a64eff6bffe8e4ddcbfdf7d9054fa1f31d6d8fb2ec22e6755bada5686a111402ef4ecaa88ce72f61b28a5719763e1444e20718b38c3f3e6141026cdc0fc6cd8ba25a8a17e1af459606f4fc1a9886ffeb2c2af32c490069f0e292a7e5519927c0788567c53cdbc0c4d2b01afed0c330898a925ac170d22a490403705eb214240a474962d03ec716c7cfe1f9413de4e1559c2eb2ab5138839fc360c69759ceab3481912a740ceb9ef1e3424ee3aa7b29aaf99c17c5cb14619a912ca23252d1c44b36fc4ae0521f2be3bbc7d771390c3e1cbf21a86074a8413574e07a685f5def695dc8213f6307662f485008c37d11cd578a9472e4a8098b1f98ac2283259b64e7c301410dc68c7e86f1a2feaddce26c89df69c487161e6d74562ba3c1b5f2f7f5613b3487086077a734a884a7e7e58aedb387676deb66eecda640bd82b8e0e5fb78cdb3aa5478624d104a6378cecb61ad011eb06042f417df15713ae7d3001e892e475ad3504ac7b09112e33dcff32c1fd2bff04e19fd98fdf9e0403eb856c44d82ee14b69eb2b65c760a9b22f444c81d322e898186e9c3e0ee18f6f0c0e2d80d29e8eaddeab943bb9dd44fbb359c6a15e0f7249b470855b311a71b74eee573782db458fd1406f4e6474d17e053344a515e0a4b27674ed5a0f36cbd4938d131659fae0ff57768be7ccf8f531c91d661fb3ee534b6c26e4cbcc2e7a767da73b0be3cb1c1d75b5ab141a03d4d01d8816393676536cf92a3151823f04d3459953039df647909ec8beccec4bbb7c0de985f795b3b0605ce55fa020766935a6dce73b099c7e93293eddc42f8603041133a1835f2d60a08be28d485044ed64bb056e44c2de3bc28198254d139af5dac2486871b61c4c949c38760cbf3a0a8dd2c151bb96ae0d645d2a0a3db17b548af385b45979c4597519c44333001ecbd683166039e0e545405ea62d9a1ec895dc5e05eaea372be2270068c829ffb1f4ec07e5cadc0b16083ff59edbffffb8020556ca211e89e6d0382be6d9a95f4f7d1eb41d86a6e61e0d3f3b1680642dfea0230736c8800312d1bf8f194600b692be0c98307a6b242000027b8d3f84c5788751f00701925f1e21561039fb684e5ae4c9ecb92e6bcacf29410d7b6ead9943d625f7fcd921066967f7cb3a4691fb169b3caebcfb5a19951b5d7c428d81eba3a6e950b7a764db3d38333879d9ee53cba30fa36f4fef5485327f7c0531a344a4bacc31321cf0355e5a9a4d5b38686b96e092b2acac1291985f8e6d0f067902d8655d1874520a6896a3cb45a8970bef8be55530d7490af03f05b9ff3447167e1e122cee573b0ae713e52dfa2cac797a812d5e76556cd57f8e2c36681618378756d92713cf71091f335045a4e3aec573511e0cd4af7d5410838243c8f8b8b861473fa5a6f539d30328f105de51aeb5587d0e9a54abc710aae85f2d865959a26c3e0ffa5bcbccaf20b52a9c1086d43940c8315e8320d3940b698bae18a5555225fdc50a668d47c909ed0cd1961bb50e4ecb7d45a02ec1bf54e31265a3144e3d2ecf5201797dc42984078436eba4ea96a9485478d96efcce33a9a6061510af7841e9559af21bc82a59f1ca3ea136be58bc701860cf8bd7a07140d1bda468aa30fe64574c5142f0849af0a321711182de1699079042358aca29c8b7c0822036b55ae4253d8c94750b9f11a7138e22cc47db4b44441d27dd4d0540312a2e3e763a68ea6e16c9728bf035d51f23f82b9fe4110148ec4a6df49330ef6795c4867b217c520851c66d376434feb0140a477b6a3cfa31b74488b7a47779e35e303d7ac40d34f3adb144fd8c1d87a03c196efd571fac3b6e4c5fbac8c1227c09baadc01f1fd6201b308e81b0a2378a2c35d1fba87574ffceed1fd7729088058cd8bba43711c5138429929c79c99d3a5c530e19b0dcd40f8e1ddf7e0a46c4a0c1aa676a20434c5f112dc67f485459080a66b15812f0d9e3f4b39e8017073a31a470ada63c1e7680316631313e891ab087ce432635171d138e6f8f73aba00279bcd57593c071ffb878ab4ce224b8392da98a8a0c9ac3a47146bb6a872240a4d6d1c25188c57104c1619e92d5e225acafe91d2b210011125c4ee22dd5b07159771119721fbfb8aa732992b3080fadb6096d82628924109e102b07546fa324a31019cb315fc0341c6793646aae4e84d1cbf556025d107dcd3dea03a22b27e46aad0b864f36a8dd91b41152a94249af3e164f8dd13f8ef1f9fc3fb87bf14f7476d23f8eb9729fc333cfdc7e1d9fd5178ffdee8f33fe0df094431f71e0e1c6ef5576d6397376d9000440dda06d3017bc0304e07ad77051eef0336385c471ff74188e8d5e383fb8fbeb9fff8db03d3bd671c33d70e2d00043d5058f054c5becf04aefb94f1b01be3079d8ccae95a347df772f6c932f059062b8d2f5ec2bf851ada360b50b511a4ed0de372c1b7c831050cd9829086dba4f7750aed2cadea004111914f3bf251c417674688bc9aef901c4ae5f114f73e3ebc3b469b96a530eb6250ae80dd4cbe699da93e133908d251b267033f5ace5565d59838258ca96b26cd34aac8858d295d6339951e16d62272ed9ed9932d04136b8da56e56160418f465959efb21db81bf3b0d8d40ed64a18b7582e481c5b6ebee911ace98e0bd695af00d988fa94abfeddc61e0af70bf6345b49de2ba20ecff42cba3b5f0df2169b446248dc1d777be6a744fabf12db0436bb00e29319b28293cf373ddb56a4d3cda0256de1eee39b1e01c83b38319a8319bd39ec5819b8a265d355f6f30a7778ba123cbb0354ae440f858832e78fcd45bae6ea2fcecc10f0eecc1d4cb2920e5cc8f1699018d1fdec19c8462ac9899068a2688da331d2e05a12cba316bd7fad83b03ff1cd57bd4840f18fa17Line truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7dff73db36b2f8effe2b507d72a594c8949df43af789a3f4a56e72cfd77c9b38e9bd19d7f7869220893545aa2465c72ff5fffe76172089af1465bbbdbb99a7696389582c168bc56277b1004723769cadaff378b12c59ff78c01e1f1c7ec3fe165d6413f67d962f5894ce00222df378b229b3bc60fd8273562e393b7ef7f6e38793ef3f7d7cf7e194cde3840fc23d40f7224918a12b58ce0b9e5ff259c83e159c6573a81617acc836f994b36936e30c7e2eb24b9ea77cc626d7d0187b73f271bf28af138eb89278cad3029b8b4a3685d20967f36c0324c529d1f0fae4f8e5dbd397d47cb8b7377af84b91c469c9267976056d3f6565bee143682b2de374c3abdfeb6453e0ffe2377b38829a8b249b44097bf094cda3a400a0285d6c922897bf11682fd8c0970278312d83a3bdbdcb2867c5753a856ea50b36ae6a84ab6cb649783fa8cb82213b0bd651318d9275cea7cb322cf3282d92a8e4c1f9e088106df2641201fa310b806f84bfae1f4207e6f1a23fdfc083384b59ffc1b22cd7eff3ec329ef17cc81ed4f8aa6703f6658fc147030c677c1e6d92b2083f17f9fc3f7904cfde462b6af4bff68f4f3fbcdaff985df0141adf52f738cb2e625ed5d56a8aaa164121f0eeb48cca78fa0a06ab789d61e37d41247e8031f3f8f3531624c0c6fd605817149bb928087f29b234a0e737c034fc5f675199674902488397973c2d8fcb3c01c62b3c2ba6d91a0696ba55f3871e86495494540bba936e92443000c7254b41a2b0972406cd73ac71f2033c3fa8badcbf8ad35976350827f0b71f4cf83ccbf9264da0a70a1dfdaa65fcb89053bfaa568acd74ca8be2558a30754f665119a968e239eb7f2570a98f95fe3de0abb8ec079f4ede11543038d2a06a3a703e3445377b5a13b2cbcfd981d90a121442775f46d3a522a51c396ac2e20706abc860ca26d9a2df23a8de90d1df309e55dfca6b1c2df19d7a7c64e1d17a67d5322adc28bf6f8e9aae3944009b3ba34e253c5d944bb6cf0ecf9bdaf5d89b55817a0571c1cb8ff18a679b52e1893540288de18297fd4a033c62c188e82fbe2be274cac7013c124d0eb4aaa1948e7e2d254639cff32cefd3bf50a6f47ec8fe7c70201fdc28e22641b70a5b47599bcf5b854d117a22e41e1997c440c3f830b83f861d1e581cdb9182b6d6ad965bb4db69f5b45dc3a9ab027c4fb2698450151b71b841e75efe00c5428b554fa143ef7ed474013ec54529ca4bb1d2c9915335e8345bad134e748cd9979b23bd0c972fdff393147ba435d894a79cfa56d8958957f8fcec5c7b0eab2f4f6cf0d535cdd820d09ea600ecc0b1ceb3329b66c9f1121623b04d34599530395f677909ec8becc644d97b606fccafbcb51d9d02e32a7d891db349ddac1739ac9927e93c93f5dc42f8a837c225b437a8e5ad11102c28d4890446d62b58adc8989ac779513204d9440b5e9958490c0fd7621127230d1fc25a9e07456566a9d8c85403b32e920b3a9a7d5183f48ab36574c9597419c549348125807d143586acc7d39e8aaa405d2c1b942db1ab18cccb55544e9704ce8051f077ffd329ac1f574b302c58ef7f96fb1fffde2348159ba804bae7ba0641db36cd4afa7dfcb617369a5b2cf0e96228aa81d037ba009639d6478098a60dfc7946b0855c2be0c9a347a6b242000027b8b3f85c5788551b00701925f1ec3561039bb684e9ae0c9e6b25cd79b9c953425cad55cfc7ec31fbfa6b968430b2fcf3bb390dfb808deb595e7d6e0ccd8caabd2246c176e86ab8512e68d9d5d5ce0ece1debf424e7d185d1b6a1f76f069a3a79009652af565a621e9e0a79eea92a4f25ad1a355c98ab9a30a3a21c8c9241882547863d836c315615bd5b04622e51b585562911ce672f1a35554307f92a00bbf5079e28e62c3c9cc5b97c0eab6b9c0fd45254f958882a517d5e669be9120b3ead67e83688a21b938c93a987889cafc0d172d26117554480352bcd5707216090f03c2e2e6a52cce16bac4d75c0687904ef2ad758af1a844e2b55e28d53302d94c7ae55a9aed20ffe5fcacbab2cbf20951a0c706d88927eb0045da62107c806533b5cb1dc94c8173794291a151fa425b43b236c138a8cfd865a4b807dbdde2ac6442bba685c2e7b1dc8c52937134b20949099ae53aa2ecac2a2c695efdc633a9a6061510af3841e9559a72ebc86a99f9ca0ea1373e5cefd80850cf8bdfc0014f56bda068aa10fcb8b688a29561092be2968b98860d11296062d8fb00816cb28e7221e82c860b52a97a129ec6423a8dc788b381c7e16e23e9e5ba220e93eae69aa0009d1c90f43a6f6a6e66c9b287f005d51f23f82b9fe4e1014f6c4a6df493376f687b890c664278a410a398ca66d869e551d004fef7c4b9bc73b3448937a4b739e39e303d75681ba9d74b22e9eb283a15502ce96afe824fdfebae4c5c7ac8c1227c0bb4db905e2c56c06a308e86b0a2378a2c3dd1cb9bb570dfcf6defd772908005fcd8bba45711c933b429129c79899c3a5f930e1bb358d40f8e9c30b3052d6253a0d633b50029ae2640ee633dac2c249c0a56b19812d0d963f4b39e8013073a30a470ada63c6a7b806cc862626d0235711d8c865c6a2e2a236ccf1f72aba00239b4d97593c051bfbfb0d699d59960625d531514195c9668128566cb6c991285c6ae32841677c03ce649191dee225a2a5e81f292d0b11105182ef2ec2bd95537119177119b2bf2f792a83b90203a8bf3546896d8222e994102e005b65a42fa31403c0395bc23fe0642cb22152257b6fe2f87503ab24da807b5a09aa2322eb27a40a17976cba5961f44650850a2589a6bc3fea7ff714fefbc76fe1c3a39f8b8783a612fcfa790cfff4cffe7174fe70103e7c30f8ed1ff0ef08bc9807873d8759fd5553d9654d1b240051bda6c2b8c71e31f4d341eb5d81c5fb88f58e56d1e77d10222a7a72f0f0f1370f9f7c7b609af78c63e4daa10580a0470a0b9ea9d8f799c0f590221e7665fca091b1719a1675db9d8c7d5a19f8248399c667afe0df42756deb09a8ae11a4ed8dc5e5825f23c71430640b421a6693ded619d4b3b4aa030445443e6d8947115f9c1121b26abe43722894c753dcfbf8f4e104d7b42c8551179d7239ec66f04d6b4cb599c8409086923d1af8d162ae2aab86c429b198ba46d20ca38a58d890c2359651e96161252237ee913dbd066762a5b1d4cdca820083aeacd2633fb476e077e74223503b59e8629d20b967b1eda6bda7863126786f2e2d5802cbc758a5df36eed0f157b8df32239a46715e10f67fa1e9d1acf0df21693447248dc1d7f73e6b744babb62db041abb30e2931ab28213cf373d3366b4d3cda04564a8ff69c58708cc1d8c108d4904d69cfe2c04d451dae9aaed618d3bb45d79165581b25b2276cac5e1b3c7eaa2d5737517ef6e0073bf668ece5149072ee478bcc80ca87f73026a1e82b46a681a211a2f60c874b4128936ec89ab93ef48ec03f47f51ed7Line truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["app.js"] = bs
|
||||
@@ -73,7 +73,7 @@ func init() {
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["favicon.png"] = bs
|
||||
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7dfb7bdb36b2e8effd2b106ddbd87b4dc9ceabf7bab6f73a76daba6d1e9f9d6c4f4fbf9efd2012925053040390b655d7fbb79f9901df0f91b295c4bbdb1f1253c46b663033981900c3bd07c7af8fdefefce6059b4573ffe0b3bd078ef3d968c48e54b8d0723a8bd8c6d1267bb4bdf3847dcfcfd5983d577aca78e0418d20d2721c474a1bb6618460d14cb0a3d7afde9e9e3c7ff7f6f5e9199b485f6c0eb1bb43df67d49d615a18a12f843764ef8c606a02cda46146c5da15cc559e60f073aa2e840e84c7c60b188cbd3c79eb9868e10beccb97ae080c0ec723e642e958b0898a012419100c3f9e1cbd7875f682861f7ee638801522c77c1e4cf7072218b060eaf030dc1f9845e0c2f0c1945eb98891f27da1f7076769c951a4fd01737d6eccfe002bf98a9f0fb04bc1bd83cf18db9b8b883377c6b511d1fe208e26ceff1de405b3280a1df13e9617fb83ff72de1d3a476a1ef2488e7d01ddc288228056272ff68537158576019f8bfdc1851497a1d251a1eaa5f4a2d9be272e800c0efdd802c46524b9ef1897fb627f67b85debc813c6d5328ca40a0a7dd5aaf1389a295dabe1cbe01c26ce078a4171e4c611932ef634d362b23f98f00bfc390c8190079f61834846be38c888c8fe60d7d738cfaf607e5fc1481b9b37377b235b2b1bc07636562a3291e6e1c8356694fd1ace6530843783040ee4063313221a543bd040834bbe5852997eb3681102c691b88a70242a616cacbc05bba647c642ee7900bd335651a4e6bbecabedf0eaeba46c021472267c2efdc52e1b7c27fc0b114997b3572216832d96bdd862871aa6668b191e1807585f4e6c17379fd19fd9ce169b3d827f8fe1df13f8f7341bbe3cc4a9450bfabec368b1ff7f0a03ccb99ecac08954b8cb76864fc5bc547788a471e62a5026e4209b8d60bd1481afb6d84b157017fe8252300a6405803c028996420388970061d64d65080e728082e7a1487b07114955a40f90abeba5383beda513e095f6d2ac676f69cf8da559cf5e4685b1d29ed09676810a2a78f96aaa2a64de65db5f97f9aaf086ba719ee6ec152a235158779183415d5c540790267202e58c63d057513614bd26f67690bd2d68958680981fcf83ac8d274de873984b1980140967ec2bf73c8503a4ce2a19e0fe943f32c6218d0eac93178cb97b3ed5a88c7114a501fce978e3d1e3675becd1936dfc6f67f3eb320535f7646c76d9e302f2297d76c22bf6247f9f12f211bc7e94bebea9e2056c160c3d0e3aadc2e7be9800b4db39a397d0dbd9ce5f13e7735f4e91fe88e4d7ddb44a099cae1175fae25cb007728e0a9d07518324c0f29535bb9cc948382433d8f412746049f75c0a4bfd27dbdb8d3d79353566f17f044aac0b0a6f68e6dcf79d12155b014a1affffb9f024671b737e95d0f4ab675f85579b5907895c810110829a00963eb06f8ad29755666cf457e07d300322c659b60c305864235c51c4703adccdeab2bf025134689931acf92c9c01a50d08150324d425c3a91a6bc1cf0d9a08be82e65a845a411b1f58706460f1065be35246b3628f969fcc90fd7594bdae10410399d259b9c9d8716f44220846c2c85a099fed118658e4c98baaadf1e202965ab2330ef646506e975030c4d85b1582486986360cbe0bf845668af00b2cb17f500da58f9e98f0d8078b01ba17544f4e39adfb76852308924e100a0ec4d14919ae8d203de5319cb1067b0f6093f3695a82fa6dc08c76eda34302f3ec0959006c469cb93f78fc68c0acb532d8d9f96a3002ecb0f76ca8b0320e76c266d203adeb5cc182dc64328459ebd82f344f912f3c92dc667891898084976021c4e114b48e3809266a1888cb02fa54116cda4805897d607f6416e0380ad201f011fee504b7e6873525c1483dcf06dad82c0f50a4f1d45f8433b49e58f6e4b83371a1e16f1c0e529a7d29e62693ffbc131089c0c0fa20f227e782fb31fc2fb48149df1f5c5f17d1c51a26bab9191cbcb36f81c7d8f51749ed2f6eca5344038d2c090a941cf9b24cd704174fabd0539741195b9e10e62f836a3d60dbe914ed60d433c98f622f151c0f5e7832b200beff3218033df6c6192783044748ad31fce3a5f17346c9c69d8b202e0149681c64a016e71094e7a90815ce61e7c4cd3caf326355140e3d8f6177b0ba2bbd48aa22c465a276418442d10b22a0caa5b094e9800abbec822723a4bc901e8acd0a50c3e2109d59e56d7a81eeaa6907d86977b723a3ec4fc5f71a1dd32e6866b0d49c1c7f481a9a591c2103f7025a4d269d10dbee6e473f58c723aea39e6c3881eab30e784e6d8f1f92827cace27e20c3caad2347ccc368d1253dd8e712a0f746b1dfac3df39264e1c707585eec6adfb250dbfa681e7c23b589609dbfdc622af0170c9cf3cb80c9090b842b8ce17af1354b68ca2eb90ed06e4a4c89dc12c105f101f43f91d393003df64c4d43c7b95d5002c677e69eb3f3a8b8ba16ca810ec267f4bf930c5b5e11aa7591d61ed5da9b3d2eac69a53a142c18a43c026ea5f0840706d6e3838c76ed03a0f95555f861610adfce30f684348835194b6cc60d1b0b01ee04bfc038541c81b907a6a80bbe18b4f0862c8f6ecc639c860430b43a934a148d0203a3dcf5b060c594a6be1d7cf43f2b564a0f3b058d13332fda282c047731b18c3eaa24574d881a4bf9206d1379356898ccf28bd2cfc28fdc722e0b8798c63ed8c9c0cb15d62f3179da1f36cc976772a7d9061ad7e83b6de63d340ac4b365f2e0a05b1c0e98f490dec9085298e5929148d1f5353639c2820d7c1a9e1c83354c53083f058f6c9fe8dde0df1f01ec8acdb954ea4af5a026086193e855aa916957b2dc800e3e0f4d6ad0856093610cf12f257c536b105f02629fcbc013576890d6baef6129170d2e66a904b4b9b9e9ea2b978382c381e434e004ba334bceb38847714ef04608eb8678d689733913607fc7c17940d6ec3bfb50b7b0fbf76580154301289fd9873bf5e5f2c0aae6b3e4a9bbb75a1fa0029b38a805060a08770cc4d8c6f535f6fb466817d8874f4581e5bfd86c866e65c8a5d7c8d1cd609f40e58f08735b49c5b1b1af60fd2b8b79557f26929fea9da2c895a53c955e9689716db43eeb6ab56235e8d32ce836f0546cc22a51d8e4b789b4241168266a94065a9a4b755b1116ceba573f98dace952f5b3d0abe00f4bd64602f431cc32849e0a2a0cda0bdd78ad3a81da9bbe36b03648e0a45d081f73754730d081f4b2d5ca4df9df04e2ddb39f86dfe2f09257f1dcae082fbb28d7b7a9325b16a1d23a75d7479a1b5ba23599a71f8946c31f5d5b8cb1dfe16ea70bf29d8b10e224ca9f76fa42f0cfb8371ff922fccab783e16fae6a6a6bf65041026c36fb17fb674f67c115167631970e4bee79f8ebe3335ef22ef8fcafd70d4f5b1f3351197faba47b4757d157b0e863d7cc5bbc275afc1ed7b3d2157ef2e046eafcf5b145500eead9d8003b65d0e01a94bf47d73b3Line truncated
|
||||
bs, _ = hex.DecodeString("1f8b080000096e8800ffec7d6b5b2337b2f0f7fc0a8d37c9c0beb40d73cb7b08b08781494292b93c30b339397972f691bb655ba16f2375030e617ffba92af5fde26e63c3b067f3610677eb5655aa2a5595a4eabd47c76f8fdefffcee159b459e7bf0d9de23cbfa6c346247413857723a8bd8c6d1267bb2bdf38c7dcfcf83317b19a829e3be0335fc48c9711c054ab30d2d048b66821dbd7df3fef4e4e587f76f4fcfd844ba627388dd1dba2ea3ee3453420b75219c21fba0050b26d04c6aa68358d982d98123183c4e830ba17ce1b0f11c0663af4fde5b3a9abb02fb72a52d7c8dc3f188d9503a166c12c40092f409861f4f8e5ebd397b45c30f3fb32cc00a91632ef7a7fb03e10f983fb57818ee0ff4dcb761787f4aaf6cc428705da1f6076769c951a4dc01b35daef5fe002bb9013f1f6097823b079f31b6e78988337bc69516d1fe208e26d6ff1fe405b3280a2df1319617fb83ffb23e1c5a478117f2488e5d01ddc288c2875627aff685331585763ef7c4fee0428acb305051a1eaa574a2d9be232e800c163d6c01e23292dcb5b4cd5db1bf33dcae75e4086d2b194632f00b7dd5aaf1389a05aa56c395fe394c9c0b1483e2c88e23266dec69a6c4647f30e117f8380c8190079f61834846ae38c888c8fe60d7d738cf6f607edfc0481b9b37377b23532b1bc074360e8248478a87235beb51f634f4a43f843783040ee4063d13221a543b5040834b3e5f50999e59340f01e3485c4538129530360e9c39bba69f8c85dc71007a6b1c4451e0edb2afb6c3abaf93b20950c89a704fbaf35d36f84eb817229236676f442c065b2c7bb1c50e154ccd16d3dcd716b0be9c982e6e3ea33fb39d2d367b02ff9ec2bf67f0ef79367c7988538316f4bdc268b1fbff0a03785c4da56f4541b8cb7686cf8557aa3b44d2585ee0073ae4209b8d60bd16be1b6cb1d781cf6df80b4a4107202b00e41148b4140a40bc0408b36e2a43709003143c0745da398848aa2275805c5d2fc5d9692f9d00afb497663d3b0b7b6e2ccd7a76322a8c03e5086568e7077e052f3798061532efb2edafcb7c557843dd58cf73f60a032d5158779183415d5c5407903ab2fcc01ac7a0afa26c287a4dec6d217b1bd02a0d013137f6fcac8d2375e872984be98314096bec06f6790a07489d5132c0fd297f648c431a1d58272f1873fb7caa5019e3288102f0a7e38d274f5f6cb127cfb6f1bf9dcdafcb1454dc91b1de654f0bc8a7f4d909afd8b3fc7d4ac827f0fa49fafaa68a17b0993f7438e8b40a9fbb6202d06ee78c5e426f673b7f4d9ccf5d3945fa23925f77d32a2570ba46d4e98b73c11e490f153af7a3064980e52b6b76399391b04866b0e925e8c092eeb91486facfb6b71b7b726a6acce0ff0494581714ce507bdc75ad12155b014a1affa7271cc9d986c7af129a7ef5e2abf06a33eb20912b3000425013c0d207e64d51fab2ca8c8dfe0abc0f6640c438cb9601068b6c842b8a184e87bb595df657208a022d3386359f8533a0b406a162804470c970aac64af0738d26821b4073254215401b175870a461f1065be35246b3628f869ff490fd7594bdae10410199d259b9c9d8716f44220846c2c858099fed118658e4c88baaadf1ea02965ab2330ef646506e965030c4d8fb200491520c6d187ce7f38bcc14e1175862fea01a4a7f3a62c263172c06e85e503d39e5b4ee9b158e20483a41283810472565b83682f494c7b0c60aec3d804d7ad3b404f5db8069659b9f1609cc8b676401b01971e6fee0e9930133d6ca6067e7abc108b0c3deb3a1c2ca38d8099b4907b4ae75050b7293c91066ad63b7d03c45bef093e436c38b4c0424bc040b210ea7a075c4893f0986beb82ca04f15c1a68d023fb10fcc4366018e233f1d007fc2bf9ce0c6fc30a62418a9e7d9401b9be5018a349ebaf37086d613cb7e59f64c5c28f81b878394665f0a4f67f29f770222e16b581f44fecbbae06e0cff0ba561d2f707d7d74574b1868e6e6e06071fcc5be03176fd4552fb8b9bf214d140234382022547ae2cd335c1c55141e804977e195b9e10e62f836a3d60dbe914ed60d433c943b1970a8e07af1c1919003f7ee98f811e7be38c93418223a4d618fef1d2f839a364e37ac28f4b40121a0719a8c53904e5792ac200e7b073e2668e5399b12a0a878ec3b03b58dd03354faa22c465a276418442d10b22a0caa53094e9800abbec822723a4bc900e8acd1250c3e2109d19e5ad7b816e07d30eb0d3ee6e4746d99f8a1f153aa65dd0cc60a93939be4b1aea591c2103f7023a984c3a2136dddd8e7eb08e475c453dd97002d5671df09c9a1eef92827c1cc4fd4086955b4596f0c268de253dd8e702a0f746b1dbac3df39264e1c71fb0bc98d5be65a136f5d13cf8462a1dc13a7fb9c502df9d3370ce2f7d2627cc17b6d09aabf9d72ca129bbe4ca47bb293125724b0417c447d0ff444e4f7cf4d833350d1de776410918d7f21c6be74971752d94031d84cbe87f2b19b6bc2254eb22ad1daab5377b5a58d34a75285830487904dc4ae108070caca70719edda0740f3abaaf0c3c214be9f61ec0969102b3296d88c6b361602dc097e8171a83802730f4c511b7c3168e10c591eddf0629c860430b43a934a148d0203a3dcf5b060c594a6be1d7cf43f2b564a0f3b058d13ed156d141682bb985846f72ac95513a2c6522e48db445e0d1a26b3fca2f45878c82de7b2708869ec829d0cbc5c61fd1293a7fd61c37c7926779a6da0718dbed366de43a340bc58240f16bac5e1804907e99d8c20855e2c1989145d5f6393232cd8c05fc39363b086690ae151f0c8f489de0dfefd11c0aed89c0ba5ae540f6a821036895ea51a997625cb0de8e0f250a7065d083619c610ff52c237b506f12520f6b9f41d71850669adfb1e9672d1e062864a409b9b9baebe723928381c484e0d4ea03d33e43c8b7814e7046f84b06e88679d58973301f677ec9ffb64cd7e303fea1676ffbe34b0622800e533f363a5be6cee1bd57c96fceaeeadd607a8c0260e6a818102c21d0331b6717d8dfdbe13ca06f6e1535160f92f369ba15b1a72e934727433d82750f91e616e2ba93836e615ac7f6531afeacf44f253bd5314b9b294a7d2cb3231ae8dd6675dad56ac067d9a05dd049e8a4d58250a9b3ceb4849128166a24669a0a5b954b51561e1ac7bf583a9ed5cf9b2d5a3e00b40df0b067632c4318c92042e0ada0cda3bad388dda915a1d5f1320b38250f81d787f4335d780f0b154c246faad84776ad97ae0b7b9bf2494fc7528fd0beeca36eee94d96c4aab5b49c76d1e59552c18a6469c6e153b2c5d40dc65deef0b75087bb4dc18e7510614abd7f235da1d91f8cbb977caedfc4de58a89b9b9afe961140980cbfc5fed9d2d9cb79449d8da5cf91fb5e7e3aface02af8bbc3f06f6dd51d7c5ced7445ceaeb01d1d67683d8b130ece106bc2b5cf716dcbeb71372f55621707b7ddea2a87c706fcd041cb0ed720828b844df37Line truncated
|
||||
gr, _ = gzip.NewReader(bytes.NewBuffer(bs))
|
||||
bs, _ = ioutil.ReadAll(gr)
|
||||
Assets["index.html"] = bs
|
||||
|
||||
+33
-1
@@ -686,9 +686,22 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
});
|
||||
if ($scope.currentRepo.Versioning && $scope.currentRepo.Versioning.Type === "simple") {
|
||||
$scope.currentRepo.simpleFileVersioning = true;
|
||||
$scope.currentRepo.FileVersioningSelector = "simple";
|
||||
$scope.currentRepo.simpleKeep = +$scope.currentRepo.Versioning.Params.keep;
|
||||
} else if ($scope.currentRepo.Versioning && $scope.currentRepo.Versioning.Type === "staggered") {
|
||||
$scope.currentRepo.staggeredFileVersioning = true;
|
||||
$scope.currentRepo.FileVersioningSelector = "staggered";
|
||||
$scope.currentRepo.staggeredMaxAge = +$scope.currentRepo.Versioning.Params.maxAge;
|
||||
$scope.currentRepo.staggeredCleanInterval = +$scope.currentRepo.Versioning.Params.cleanInterval;
|
||||
$scope.currentRepo.staggeredVersionsPath = $scope.currentRepo.Versioning.Params.versionsPath;
|
||||
} else {
|
||||
$scope.currentRepo.FileVersioningSelector = "none";
|
||||
}
|
||||
$scope.currentRepo.simpleKeep = $scope.currentRepo.simpleKeep || 5;
|
||||
$scope.currentRepo.staggeredMaxAge = $scope.currentRepo.staggeredMaxAge || 31536000;
|
||||
$scope.currentRepo.staggeredCleanInterval = $scope.currentRepo.staggeredCleanInterval || 3600;
|
||||
$scope.currentRepo.staggeredVersionsPath = $scope.currentRepo.staggeredVersionsPath || "";
|
||||
|
||||
$scope.editingExisting = true;
|
||||
$scope.repoEditor.$setPristine();
|
||||
$('#editRepo').modal();
|
||||
@@ -696,6 +709,11 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
|
||||
$scope.addRepo = function () {
|
||||
$scope.currentRepo = {selectedNodes: {}};
|
||||
$scope.currentRepo.FileVersioningSelector = "none";
|
||||
$scope.currentRepo.simpleKeep = 5;
|
||||
$scope.currentRepo.staggeredMaxAge = 31536000;
|
||||
$scope.currentRepo.staggeredCleanInterval = 3600;
|
||||
$scope.currentRepo.staggeredVersionsPath = "";
|
||||
$scope.editingExisting = false;
|
||||
$scope.repoEditor.$setPristine();
|
||||
$('#editRepo').modal();
|
||||
@@ -715,7 +733,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
}
|
||||
delete repoCfg.selectedNodes;
|
||||
|
||||
if (repoCfg.simpleFileVersioning) {
|
||||
if (repoCfg.FileVersioningSelector === "simple") {
|
||||
repoCfg.Versioning = {
|
||||
'Type': 'simple',
|
||||
'Params': {
|
||||
@@ -724,6 +742,20 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http, $translate, $loca
|
||||
};
|
||||
delete repoCfg.simpleFileVersioning;
|
||||
delete repoCfg.simpleKeep;
|
||||
} else if (repoCfg.FileVersioningSelector === "staggered") {
|
||||
repoCfg.Versioning = {
|
||||
'Type': 'staggered',
|
||||
'Params': {
|
||||
'maxAge': '' + repoCfg.staggeredMaxAge,
|
||||
'cleanInterval': '' + repoCfg.staggeredCleanInterval,
|
||||
'versionsPath': '' + repoCfg.staggeredVersionsPath,
|
||||
}
|
||||
};
|
||||
delete repoCfg.staggeredFileVersioning;
|
||||
delete repoCfg.staggeredMaxAge;
|
||||
delete repoCfg.staggeredCleanInterval;
|
||||
delete repoCfg.staggeredVersionsPath;
|
||||
|
||||
} else {
|
||||
delete repoCfg.Versioning;
|
||||
}
|
||||
|
||||
+39
-5
@@ -540,14 +540,25 @@
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<label translate>File Versioning</label>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="currentRepo.simpleFileVersioning"> <span translate>File Versioning</span>
|
||||
<input type="radio" ng-model="currentRepo.FileVersioningSelector" value="none"> <span translate>No File Versioning</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" ng-model="currentRepo.FileVersioningSelector" value="simple"> <span translate>Simple File Versioning</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input type="radio" ng-model="currentRepo.FileVersioningSelector" value="staggered"> <span translate>Staggered File Versioning</span>
|
||||
</label>
|
||||
</div>
|
||||
<p translate class="help-block">Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentRepo.simpleFileVersioning" ng-class="{'has-error': repoEditor.simpleKeep.$invalid && repoEditor.simpleKeep.$dirty}">
|
||||
<div class="form-group" ng-if="currentRepo.FileVersioningSelector=='simple'" ng-class="{'has-error': repoEditor.simpleKeep.$invalid && repoEditor.simpleKeep.$dirty}">
|
||||
<p translate class="help-block">Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.</p>
|
||||
<label translate for="simpleKeep">Keep Versions</label>
|
||||
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentRepo.simpleKeep" required min="1"></input>
|
||||
<p class="help-block">
|
||||
@@ -556,7 +567,30 @@
|
||||
<span translate ng-if="repoEditor.simpleKeep.$error.min && repoEditor.simpleKeep.$dirty">You must keep at least one version.</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="currentRepo.FileVersioningSelector=='staggered'" ng-class="{'has-error': repoEditor.staggeredMaxAge.$invalid && repoEditor.staggeredMaxAge.$dirty}">
|
||||
<p translate class="help-block">Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing. Versions are automatically deleted if they are over the set maximum age or too many files are in a interval.</p>
|
||||
<p translate class="help-block">The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.</p>
|
||||
<label translate for="staggeredMaxAge">Maximum Age</label>
|
||||
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentRepo.staggeredMaxAge" required></input>
|
||||
<p class="help-block">
|
||||
<span translate ng-if="repoEditor.staggeredMaxAge.$valid || repoEditor.staggeredMaxAge.$pristine">The maximum time to keep a version in seconds, -1 keeps versions forever.</span>
|
||||
<span translate ng-if="repoEditor.staggeredMaxAge.$error.required && repoEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentRepo.FileVersioningSelector=='staggered'" ng-class="{'has-error': repoEditor.staggeredCleanInterval.$invalid && repoEditor.staggeredCleanInterval.$dirty}">
|
||||
<label translate for="staggeredCleanInterval">Clean Interval</label>
|
||||
<input name="staggeredCleanInterval" id="staggeredCleanInterval" class="form-control" type="number" ng-model="currentRepo.staggeredCleanInterval" required min="1"></input>
|
||||
<p class="help-block">
|
||||
<span translate ng-if="repoEditor.staggeredCleanInterval.$valid || repoEditor.staggeredCleanInterval.$pristine">The interval to clean versions in seconds.</span>
|
||||
<span translate ng-if="repoEditor.staggeredCleanInterval.$error.required && repoEditor.staggeredCleanInterval.$dirty">The clean interval must be a number and cannot be blank.</span>
|
||||
<span translate ng-if="repoEditor.staggeredCleanInterval.$error.min && repoEditor.staggeredCleanInterval.$dirty">Clean Interval has to be greater than 0.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-if="currentRepo.FileVersioningSelector=='staggered'">
|
||||
<label translate for="staggeredVersionsPath">Versions Path</label>
|
||||
<input name="staggeredVersionsPath" placeholder="" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentRepo.staggeredVersionsPath"></input>
|
||||
<p translate class="help-block">Path where versions should be stored, leave empty to put them in .stversions folder in the repository.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+8
-4
@@ -47,11 +47,15 @@ func NewSimple(repoID, repoPath string, params map[string]string) Versioner {
|
||||
// nil, the named file does not exist any more (has been archived).
|
||||
func (v Simple) Archive(filePath string) error {
|
||||
_, err := os.Stat(filePath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
if debug {
|
||||
l.Debugln("not archiving nonexistent file", filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if debug {
|
||||
l.Debugln("not archiving nonexistent file", filePath)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
versionsDir := filepath.Join(v.repoPath, ".stversions")
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package versioner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/syncthing/syncthing/osutil"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register the constructor for this type of versioner with the name "staggered"
|
||||
Factories["staggered"] = NewStaggered
|
||||
}
|
||||
|
||||
type Interval struct {
|
||||
step int64
|
||||
end int64
|
||||
}
|
||||
|
||||
// The type holds our configuration
|
||||
type Staggered struct {
|
||||
versionsPath string
|
||||
cleanInterval int64
|
||||
repoPath string
|
||||
interval [4]Interval
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
// Check if file or dir
|
||||
func isFile(path string) bool {
|
||||
fileordir := path
|
||||
file, err := os.Open(fileordir)
|
||||
if err != nil {
|
||||
l.Infoln("versioner isFile:", err)
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
l.Infoln("versioner isFile:", err)
|
||||
return false
|
||||
}
|
||||
return fileInfo.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// The constructor function takes a map of parameters and creates the type.
|
||||
func NewStaggered(repoID, repoPath string, params map[string]string) Versioner {
|
||||
|
||||
maxAge, err := strconv.ParseInt(params["maxAge"], 10, 0)
|
||||
if err != nil {
|
||||
maxAge = 31536000 // Default: ~1 year
|
||||
}
|
||||
cleanInterval, err := strconv.ParseInt(params["cleanInterval"], 10, 0)
|
||||
if err != nil {
|
||||
cleanInterval = 3600 // Default: clean once per hour
|
||||
}
|
||||
|
||||
// Use custom path if set, otherwise .stversions in repoPath
|
||||
var versionsDir string
|
||||
if params["versionsPath"] == "" {
|
||||
if debug {
|
||||
l.Debugln("using default dir .stversions")
|
||||
}
|
||||
versionsDir = filepath.Join(repoPath, ".stversions")
|
||||
} else {
|
||||
if debug {
|
||||
l.Debugln("using dir", params["versionsPath"])
|
||||
}
|
||||
versionsDir = params["versionsPath"]
|
||||
}
|
||||
|
||||
var mutex sync.Mutex
|
||||
s := Staggered{
|
||||
versionsPath: versionsDir,
|
||||
cleanInterval: cleanInterval,
|
||||
repoPath: repoPath,
|
||||
interval: [4]Interval{
|
||||
Interval{30, 3600}, // first hour -> 30 sec between versions
|
||||
Interval{3600, 86400}, // next day -> 1 h between versions
|
||||
Interval{86400, 2592000}, // next 30 days -> 1 day between versions
|
||||
Interval{604800, maxAge}, // next year -> 1 week between versions
|
||||
},
|
||||
mutex: &mutex,
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugf("instantiated %#v", s)
|
||||
}
|
||||
|
||||
go func() {
|
||||
s.clean()
|
||||
for _ = range time.Tick(time.Duration(cleanInterval) * time.Second) {
|
||||
s.clean()
|
||||
}
|
||||
}()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (v Staggered) clean() {
|
||||
if debug {
|
||||
l.Debugln("Versioner clean: Waiting for lock on", v.versionsPath)
|
||||
}
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
if debug {
|
||||
l.Debugln("Versioner clean: Cleaning", v.versionsPath)
|
||||
}
|
||||
|
||||
_, err := os.Stat(v.versionsPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if debug {
|
||||
l.Debugln("creating versions dir", v.versionsPath)
|
||||
}
|
||||
os.MkdirAll(v.versionsPath, 0755)
|
||||
osutil.HideFile(v.versionsPath)
|
||||
} else {
|
||||
l.Warnln("Versioner: can't create versions dir",err)
|
||||
}
|
||||
}
|
||||
|
||||
// Using keys of map as set
|
||||
clean_filelist := make(map[string]struct{})
|
||||
clean_emptyDirs := make(map[string]struct{})
|
||||
|
||||
err = filepath.Walk(v.versionsPath, func(path string, f os.FileInfo, err error) error {
|
||||
switch mode := f.Mode(); {
|
||||
case mode.IsDir():
|
||||
files, _ := ioutil.ReadDir(path)
|
||||
if len(files) == 0 {
|
||||
clean_emptyDirs[path] = struct{}{}
|
||||
}
|
||||
case mode.IsRegular():
|
||||
extension := filepath.Ext(path)
|
||||
name := path[0 : len(path)-len(extension)]
|
||||
clean_filelist[name] = struct{}{}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
l.Warnln("Versioner: error scanning versions dir",err)
|
||||
}
|
||||
|
||||
for k, _ := range clean_filelist {
|
||||
versions, err := filepath.Glob(k + ".v[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]")
|
||||
if err != nil {
|
||||
l.Warnln("Versioner: error finding versions for", k, err)
|
||||
}
|
||||
sort.Strings(versions)
|
||||
expire(versions, v)
|
||||
}
|
||||
for k, _ := range clean_emptyDirs {
|
||||
if k == v.versionsPath {
|
||||
if debug {
|
||||
l.Debugln("Cleaner: versions dir is empty, don't delete", k)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if debug {
|
||||
l.Debugln("Cleaner: deleting empty directory", k)
|
||||
}
|
||||
err = os.Remove(k)
|
||||
if err != nil {
|
||||
l.Warnln("Versioner: can't remove directory", k, err)
|
||||
}
|
||||
}
|
||||
if debug {
|
||||
l.Debugln("Cleaner: Finished cleaning", v.versionsPath)
|
||||
}
|
||||
}
|
||||
|
||||
func expire(versions []string, v Staggered) {
|
||||
if debug {
|
||||
l.Debugln("Versioner: Expiring versions", versions)
|
||||
}
|
||||
now := time.Now().Unix()
|
||||
var prevAge int64
|
||||
firstFile := true
|
||||
for _, file := range versions {
|
||||
if isFile(file) {
|
||||
versiondate, err := strconv.ParseInt(strings.Replace(filepath.Ext(file), ".v", "", 1), 10, 0)
|
||||
if err != nil {
|
||||
l.Warnln("Versioner expire: file", file, "is invalid")
|
||||
continue
|
||||
}
|
||||
age := now - versiondate
|
||||
|
||||
var usedInterval Interval
|
||||
for _, usedInterval = range v.interval { // Find the interval the file fits in
|
||||
if age < usedInterval.end {
|
||||
break
|
||||
}
|
||||
}
|
||||
if lastIntv := v.interval[len(v.interval)-1]; lastIntv.end != -1 && age > lastIntv.end {
|
||||
if debug {
|
||||
l.Debugln("Versioner: File over maximum age -> delete ", file)
|
||||
}
|
||||
err = os.Remove(file)
|
||||
if err != nil {
|
||||
l.Warnln("Versioner: can't remove file", file, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if firstFile {
|
||||
prevAge = age
|
||||
firstFile = false
|
||||
continue
|
||||
}
|
||||
|
||||
if prevAge-age < usedInterval.step {
|
||||
if debug {
|
||||
l.Debugln("too many files in step -> delete", file)
|
||||
}
|
||||
err = os.Remove(file)
|
||||
if err != nil {
|
||||
l.Warnln("Versioner: can't remove file", file, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
prevAge = age
|
||||
|
||||
} else {
|
||||
l.Warnln("Versioner: folder", file, "is named like a file version")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Move away the named file to a version archive. If this function returns
|
||||
// nil, the named file does not exist any more (has been archived).
|
||||
func (v Staggered) Archive(filePath string) error {
|
||||
if debug {
|
||||
l.Debugln("Waiting for lock on ", v.versionsPath)
|
||||
}
|
||||
v.mutex.Lock()
|
||||
defer v.mutex.Unlock()
|
||||
|
||||
_, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if debug {
|
||||
l.Debugln("not archiving nonexistent file", filePath)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = os.Stat(v.versionsPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if debug {
|
||||
l.Debugln("creating versions dir", v.versionsPath)
|
||||
}
|
||||
os.MkdirAll(v.versionsPath, 0755)
|
||||
osutil.HideFile(v.versionsPath)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln("archiving", filePath)
|
||||
}
|
||||
|
||||
file := filepath.Base(filePath)
|
||||
inRepoPath, err := filepath.Rel(v.repoPath, filepath.Dir(filePath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dir := filepath.Join(v.versionsPath, inRepoPath)
|
||||
err = os.MkdirAll(dir, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
ver := file + ".v" + fmt.Sprintf("%010d", time.Now().Unix())
|
||||
dst := filepath.Join(dir, ver)
|
||||
if debug {
|
||||
l.Debugln("moving to", dst)
|
||||
}
|
||||
err = osutil.Rename(filePath, dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versions, err := filepath.Glob(filepath.Join(dir, file+".v[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]"))
|
||||
if err != nil {
|
||||
l.Warnln("Versioner: error finding versions for", file, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.Strings(versions)
|
||||
expire(versions, v)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user