Backend: Add WebDAV client and dummy test server #225
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
d80a69e962
commit
b86f68c3f3
16 changed files with 545 additions and 0 deletions
3
Makefile
3
Makefile
|
@ -132,6 +132,9 @@ docker-tensorflow:
|
|||
docker-tidb:
|
||||
scripts/docker-build.sh tidb $(TIDB_VERSION)
|
||||
scripts/docker-push.sh tidb $(TIDB_VERSION)
|
||||
docker-webdav:
|
||||
scripts/docker-build.sh webdav $(DOCKER_TAG)
|
||||
scripts/docker-push.sh webdav $(DOCKER_TAG)
|
||||
lint-js:
|
||||
(cd frontend && npm run lint)
|
||||
fmt-js:
|
||||
|
|
|
@ -50,3 +50,6 @@ services:
|
|||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
webdav-dummy:
|
||||
image: photoprism/webdav:20200327
|
||||
|
|
|
@ -53,3 +53,6 @@ services:
|
|||
MYSQL_USER: photoprism
|
||||
MYSQL_PASSWORD: photoprism
|
||||
MYSQL_DATABASE: photoprism
|
||||
|
||||
webdav-dummy:
|
||||
image: photoprism/webdav:20200327
|
||||
|
|
16
docker/webdav/Dockerfile
Normal file
16
docker/webdav/Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
FROM golang:1.14
|
||||
|
||||
LABEL maintainer="Michael Mayer <michael@liquidbytes.net>"
|
||||
|
||||
# Set up project directory
|
||||
WORKDIR "/webdav"
|
||||
|
||||
RUN go get github.com/hacdias/webdav
|
||||
|
||||
# Expose HTTP port
|
||||
EXPOSE 80
|
||||
|
||||
COPY /docker/webdav/config.yml /webdav/config.yml
|
||||
COPY /docker/webdav/files /webdav/files
|
||||
|
||||
CMD webdav -c /webdav/config.yml
|
16
docker/webdav/config.yml
Normal file
16
docker/webdav/config.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Server related settings
|
||||
address: 0.0.0.0
|
||||
port: 80
|
||||
auth: true
|
||||
tls: false
|
||||
prefix: /
|
||||
|
||||
# Default settings
|
||||
scope: /webdav/files
|
||||
modify: true
|
||||
rules: []
|
||||
|
||||
# Accounts
|
||||
users:
|
||||
- username: admin
|
||||
password: photoprism
|
BIN
docker/webdav/files/Photos/2020/03/iphone_7.heic
Normal file
BIN
docker/webdav/files/Photos/2020/03/iphone_7.heic
Normal file
Binary file not shown.
139
docker/webdav/files/Photos/2020/03/iphone_7.json
Normal file
139
docker/webdav/files/Photos/2020/03/iphone_7.json
Normal file
|
@ -0,0 +1,139 @@
|
|||
[{
|
||||
"SourceFile": "IMG_3963.HEIC",
|
||||
"ExifTool": {
|
||||
"ExifToolVersion": 10.80
|
||||
},
|
||||
"File": {
|
||||
"FileName": "IMG_3963.HEIC",
|
||||
"Directory": ".",
|
||||
"FileSize": "767 kB",
|
||||
"FileModifyDate": "2019:06:06 03:46:59+00:00",
|
||||
"FileAccessDate": "2019:06:06 03:49:46+00:00",
|
||||
"FileInodeChangeDate": "2019:06:06 03:47:56+00:00",
|
||||
"FilePermissions": "rw-r--r--",
|
||||
"FileType": "HEIC",
|
||||
"FileTypeExtension": "heic",
|
||||
"MIMEType": "image/heic",
|
||||
"ExifByteOrder": "Big-endian (Motorola, MM)",
|
||||
"ImageWidth": 4032,
|
||||
"ImageHeight": 3024
|
||||
},
|
||||
"QuickTime": {
|
||||
"MajorBrand": "High Efficiency Image Format HEVC still image (.HEIC)",
|
||||
"MinorVersion": "0.0.0",
|
||||
"CompatibleBrands": ["mif1","heic"],
|
||||
"HandlerType": "Picture",
|
||||
"PrimaryItemReference": 49,
|
||||
"ImageSpatialExtent": "4032x3024",
|
||||
"ImagePixelDepth": "8 8 8",
|
||||
"MovieDataSize": 782453,
|
||||
"MovieDataOffset": 3290
|
||||
},
|
||||
"EXIF": {
|
||||
"Make": "Apple",
|
||||
"Model": "iPhone 7",
|
||||
"Orientation": "Rotate 90 CW",
|
||||
"XResolution": 72,
|
||||
"YResolution": 72,
|
||||
"ResolutionUnit": "inches",
|
||||
"Software": "ProCam 10.5.8",
|
||||
"ModifyDate": "2018:09:10 12:16:13",
|
||||
"ExposureTime": "1/4000",
|
||||
"FNumber": 1.8,
|
||||
"ExposureProgram": "Program AE",
|
||||
"ISO": 20,
|
||||
"ExifVersion": "0221",
|
||||
"DateTimeOriginal": "2018:09:10 12:16:13",
|
||||
"CreateDate": "2018:09:10 12:16:13",
|
||||
"ComponentsConfiguration": "Y, Cb, Cr, -",
|
||||
"ShutterSpeedValue": "1/4000",
|
||||
"ApertureValue": 1.8,
|
||||
"BrightnessValue": 11.39478764,
|
||||
"ExposureCompensation": 0,
|
||||
"MeteringMode": "Multi-segment",
|
||||
"Flash": "Off, Did not fire",
|
||||
"FocalLength": "4.0 mm",
|
||||
"SubjectArea": "2009 1510 2213 1331",
|
||||
"SubSecTimeOriginal": "023",
|
||||
"SubSecTimeDigitized": "023",
|
||||
"FlashpixVersion": "0100",
|
||||
"ColorSpace": "Uncalibrated",
|
||||
"ExifImageWidth": 4032,
|
||||
"ExifImageHeight": 3024,
|
||||
"SensingMethod": "One-chip color area",
|
||||
"SceneType": "Directly photographed",
|
||||
"ExposureMode": "Auto",
|
||||
"WhiteBalance": "Auto",
|
||||
"DigitalZoomRatio": 1,
|
||||
"FocalLengthIn35mmFormat": "74 mm",
|
||||
"SceneCaptureType": "Standard",
|
||||
"LensInfo": "3.99mm f/1.8",
|
||||
"LensMake": "Apple",
|
||||
"LensModel": "iPhone 7 back camera 3.99mm f/1.8",
|
||||
"GPSLatitudeRef": "North",
|
||||
"GPSLatitude": "34 deg 47' 50.82\"",
|
||||
"GPSLongitudeRef": "East",
|
||||
"GPSLongitude": "134 deg 45' 52.68\""
|
||||
},
|
||||
"MakerNotes": {
|
||||
"RunTimeFlags": "Valid",
|
||||
"RunTimeValue": 67281333929291,
|
||||
"RunTimeScale": 1000000000,
|
||||
"RunTimeEpoch": 0,
|
||||
"AccelerationVector": "-0.005772667605 -0.993299298 0.1439166166"
|
||||
},
|
||||
"XMP": {
|
||||
"XMP": "base64:PD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0ZURhdGU9IjIwMTgtMDktMTBUMTI6MTY6MTMiIHhtcDpNb2RpZnlEYXRlPSIyMDE4LTA5LTEwVDEyOjE2OjEzIiB4bXA6Q3JlYXRvclRvb2w9IlByb0NhbSAxMC41LjgiIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAxOC0wOS0xMFQxMjoxNjoxMyIvPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDw/eHBhY2tldCBlbmQ9InciPz4A",
|
||||
"XMPToolkit": "XMP Core 5.4.0",
|
||||
"CreateDate": "2018:09:10 12:16:13",
|
||||
"ModifyDate": "2018:09:10 12:16:13",
|
||||
"CreatorTool": "ProCam 10.5.8",
|
||||
"DateCreated": "2018:09:10 12:16:13"
|
||||
},
|
||||
"ICC_Profile": {
|
||||
"ProfileCMMType": "Apple Computer Inc.",
|
||||
"ProfileVersion": "4.0.0",
|
||||
"ProfileClass": "Display Device Profile",
|
||||
"ColorSpaceData": "RGB ",
|
||||
"ProfileConnectionSpace": "XYZ ",
|
||||
"ProfileDateTime": "2017:07:07 13:22:32",
|
||||
"ProfileFileSignature": "acsp",
|
||||
"PrimaryPlatform": "Apple Computer Inc.",
|
||||
"CMMFlags": "Not Embedded, Independent",
|
||||
"DeviceManufacturer": "Apple Computer Inc.",
|
||||
"DeviceModel": "",
|
||||
"DeviceAttributes": "Reflective, Glossy, Positive, Color",
|
||||
"RenderingIntent": "Perceptual",
|
||||
"ConnectionSpaceIlluminant": "0.9642 1 0.82491",
|
||||
"ProfileCreator": "Apple Computer Inc.",
|
||||
"ProfileID": "ca1a9582257f104d389913d5d1ea1582",
|
||||
"ProfileDescription": "Display P3",
|
||||
"ProfileCopyright": "Copyright Apple Inc., 2017",
|
||||
"MediaWhitePoint": "0.95045 1 1.08905",
|
||||
"RedMatrixColumn": "0.51512 0.2412 -0.00105",
|
||||
"GreenMatrixColumn": "0.29198 0.69225 0.04189",
|
||||
"BlueMatrixColumn": "0.1571 0.06657 0.78407",
|
||||
"RedTRC": "base64:cGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACls=",
|
||||
"ChromaticAdaptation": "1.04788 0.02292 -0.0502 0.02959 0.99048 -0.01706 -0.00923 0.01508 0.75168",
|
||||
"BlueTRC": "base64:cGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACls=",
|
||||
"GreenTRC": "base64:cGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACls="
|
||||
},
|
||||
"Composite": {
|
||||
"Aperture": 1.8,
|
||||
"GPSLatitude": "34 deg 47' 50.82\" N",
|
||||
"GPSLongitude": "134 deg 45' 52.68\" E",
|
||||
"GPSPosition": "34 deg 47' 50.82\" N, 134 deg 45' 52.68\" E",
|
||||
"ImageSize": "4032x3024",
|
||||
"Megapixels": 12.2,
|
||||
"RunTimeSincePowerUp": "18:41:21",
|
||||
"ScaleFactor35efl": 18.5,
|
||||
"ShutterSpeed": "1/4000",
|
||||
"SubSecCreateDate": "2018:09:10 12:16:13.023",
|
||||
"SubSecDateTimeOriginal": "2018:09:10 12:16:13.023",
|
||||
"CircleOfConfusion": "0.002 mm",
|
||||
"FOV": "27.3 deg",
|
||||
"FocalLength35efl": "4.0 mm (35 mm equivalent: 74.0 mm)",
|
||||
"HyperfocalDistance": "5.46 m",
|
||||
"LightValue": 16.0
|
||||
}
|
||||
}]
|
101
docker/webdav/files/Photos/2020/03/iphone_7.xmp
Normal file
101
docker/webdav/files/Photos/2020/03/iphone_7.xmp
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
|
||||
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 10.80'>
|
||||
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
<exif:ApertureValue>13166/7763</exif:ApertureValue>
|
||||
<exif:BrightnessValue>11805/1036</exif:BrightnessValue>
|
||||
<exif:ColorSpace>65535</exif:ColorSpace>
|
||||
<exif:ComponentsConfiguration>
|
||||
<rdf:Seq>
|
||||
<rdf:li>1</rdf:li>
|
||||
<rdf:li>2</rdf:li>
|
||||
<rdf:li>3</rdf:li>
|
||||
<rdf:li>0</rdf:li>
|
||||
</rdf:Seq>
|
||||
</exif:ComponentsConfiguration>
|
||||
<exif:DateTimeOriginal>2018-09-10T12:16:13</exif:DateTimeOriginal>
|
||||
<exif:DigitalZoomRatio>1/1</exif:DigitalZoomRatio>
|
||||
<exif:ExifVersion>0221</exif:ExifVersion>
|
||||
<exif:ExposureBiasValue>0/1</exif:ExposureBiasValue>
|
||||
<exif:ExposureMode>0</exif:ExposureMode>
|
||||
<exif:ExposureProgram>2</exif:ExposureProgram>
|
||||
<exif:ExposureTime>1/4000</exif:ExposureTime>
|
||||
<exif:FNumber>9/5</exif:FNumber>
|
||||
<exif:Flash rdf:parseType='Resource'>
|
||||
<exif:Fired>False</exif:Fired>
|
||||
<exif:Function>False</exif:Function>
|
||||
<exif:Mode>2</exif:Mode>
|
||||
<exif:RedEyeMode>False</exif:RedEyeMode>
|
||||
<exif:Return>0</exif:Return>
|
||||
</exif:Flash>
|
||||
<exif:FlashpixVersion>0100</exif:FlashpixVersion>
|
||||
<exif:FocalLength>4/1</exif:FocalLength>
|
||||
<exif:FocalLengthIn35mmFilm>74</exif:FocalLengthIn35mmFilm>
|
||||
<exif:GPSLatitude>34,47.847000N</exif:GPSLatitude>
|
||||
<exif:GPSLongitude>134,45.878000E</exif:GPSLongitude>
|
||||
<exif:ISOSpeedRatings>
|
||||
<rdf:Seq>
|
||||
<rdf:li>20</rdf:li>
|
||||
</rdf:Seq>
|
||||
</exif:ISOSpeedRatings>
|
||||
<exif:MeteringMode>5</exif:MeteringMode>
|
||||
<exif:PixelXDimension>4032</exif:PixelXDimension>
|
||||
<exif:PixelYDimension>3024</exif:PixelYDimension>
|
||||
<exif:SceneCaptureType>0</exif:SceneCaptureType>
|
||||
<exif:SceneType>1</exif:SceneType>
|
||||
<exif:SensingMethod>2</exif:SensingMethod>
|
||||
<exif:ShutterSpeedValue>18535/1549</exif:ShutterSpeedValue>
|
||||
<exif:SubjectArea>
|
||||
<rdf:Seq>
|
||||
<rdf:li>2009</rdf:li>
|
||||
<rdf:li>1510</rdf:li>
|
||||
<rdf:li>2213</rdf:li>
|
||||
<rdf:li>1331</rdf:li>
|
||||
</rdf:Seq>
|
||||
</exif:SubjectArea>
|
||||
<exif:WhiteBalance>0</exif:WhiteBalance>
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:exifEX='http://cipa.jp/exif/1.0/'>
|
||||
<exifEX:LensMake>Apple</exifEX:LensMake>
|
||||
<exifEX:LensModel>iPhone 7 back camera 3.99mm f/1.8</exifEX:LensModel>
|
||||
<exifEX:LensSpecification>
|
||||
<rdf:Seq>
|
||||
<rdf:li>399/100</rdf:li>
|
||||
<rdf:li>399/100</rdf:li>
|
||||
<rdf:li>9/5</rdf:li>
|
||||
<rdf:li>9/5</rdf:li>
|
||||
</rdf:Seq>
|
||||
</exifEX:LensSpecification>
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:photoshop='http://ns.adobe.com/photoshop/1.0/'>
|
||||
<photoshop:DateCreated>2018-09-10T12:16:13</photoshop:DateCreated>
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
<tiff:ImageLength>3024</tiff:ImageLength>
|
||||
<tiff:ImageWidth>4032</tiff:ImageWidth>
|
||||
<tiff:Make>Apple</tiff:Make>
|
||||
<tiff:Model>iPhone 7</tiff:Model>
|
||||
<tiff:Orientation>6</tiff:Orientation>
|
||||
<tiff:ResolutionUnit>2</tiff:ResolutionUnit>
|
||||
<tiff:Software>ProCam 10.5.8</tiff:Software>
|
||||
<tiff:XResolution>72/1</tiff:XResolution>
|
||||
<tiff:YResolution>72/1</tiff:YResolution>
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:xmp='http://ns.adobe.com/xap/1.0/'>
|
||||
<xmp:CreateDate>2018-09-10T12:16:13</xmp:CreateDate>
|
||||
<xmp:CreatorTool>ProCam 10.5.8</xmp:CreatorTool>
|
||||
<xmp:ModifyDate>2018-09-10T12:16:13</xmp:ModifyDate>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<?xpacket end='w'?>
|
BIN
docker/webdav/files/Photos/2020/03/ocean_cyan.jpg
Normal file
BIN
docker/webdav/files/Photos/2020/03/ocean_cyan.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
BIN
docker/webdav/files/Photos/IMG_4120.JPG
Normal file
BIN
docker/webdav/files/Photos/IMG_4120.JPG
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
BIN
docker/webdav/files/Photos/IMG_E4120.JPG
Normal file
BIN
docker/webdav/files/Photos/IMG_E4120.JPG
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
1
go.mod
1
go.mod
|
@ -59,6 +59,7 @@ require (
|
|||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1
|
||||
github.com/tensorflow/tensorflow v1.14.0
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
||||
github.com/twinj/uuid v1.0.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -308,6 +308,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1 h1:TPyHV/OgChqNcnYqCoCvIFjR9TU60gFXXBKnhOBzVEI=
|
||||
github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1/go.mod h1:gCcfDlA1Y7GqOaeEKw5l9dOGx1VLdc/HuQSlQAaZ30s=
|
||||
github.com/tensorflow/tensorflow v1.14.0 h1:g0W2+f/RybcvmrTjPLTwXkfr/BsDGUd8FKT6ZzojOMo=
|
||||
github.com/tensorflow/tensorflow v1.14.0/go.mod h1:itOSERT4trABok4UOoG+X4BoKds9F3rIsySdn+Lvu90=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
|
||||
|
|
BIN
internal/share/webdav/testdata/example.jpg
vendored
Normal file
BIN
internal/share/webdav/testdata/example.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
146
internal/share/webdav/webdav.go
Normal file
146
internal/share/webdav/webdav.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
Package entity implementing sharing with WebDAV servers.
|
||||
|
||||
Additional information can be found in our Developer Guide:
|
||||
|
||||
https://github.com/photoprism/photoprism/wiki
|
||||
*/
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/studio-b12/gowebdav"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
|
||||
type Client struct {
|
||||
client *gowebdav.Client
|
||||
}
|
||||
|
||||
// Connect creates a new WebDAV client.
|
||||
func Connect(url, user, pass string) Client {
|
||||
clt := gowebdav.NewClient(url, user, pass)
|
||||
|
||||
result := Client{client: clt}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Files returns all files in path as string slice.
|
||||
func (c Client) Files(path string) (result []string, err error) {
|
||||
files, err := c.client.ReadDir(path)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.Mode().IsRegular() { continue }
|
||||
result = append(result, fmt.Sprintf("%s/%s", path, file.Name()))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Directories returns all sub directories in path as string slice.
|
||||
func (c Client) Directories(path string) (result []string, err error) {
|
||||
files, err := c.client.ReadDir(path)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.Mode().IsDir() { continue }
|
||||
result = append(result, fmt.Sprintf("%s/%s", path, file.Name()))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Download downloads a single file to the given location.
|
||||
func (c Client) Download(from, to string) error {
|
||||
dir := path.Dir(to)
|
||||
dirInfo, err := os.Stat(dir)
|
||||
|
||||
if err != nil {
|
||||
// Create directory
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("webdav: can't create %s (%s)", dir, err)
|
||||
}
|
||||
} else if !dirInfo.IsDir() {
|
||||
return fmt.Errorf("webdav: %s is not a directory", dir)
|
||||
}
|
||||
|
||||
bytes, err := c.client.Read(from)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(to, bytes, 0644)
|
||||
}
|
||||
|
||||
// DownloadDir downloads all files from a remote to a local directory.
|
||||
func (c Client) DownloadDir(from, to string, recursive bool) (errs []error) {
|
||||
files, err := c.Files(from)
|
||||
|
||||
if err != nil {
|
||||
return append(errs, err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
dest := to + string(os.PathSeparator) + file
|
||||
|
||||
if _, err := os.Stat(dest); err == nil {
|
||||
// File exists
|
||||
msg := fmt.Errorf("webdav: %s exists", dest)
|
||||
errs = append(errs, msg)
|
||||
log.Error(msg)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := c.Download(file, dest); err != nil {
|
||||
msg := fmt.Errorf("webdav: %s", err)
|
||||
errs = append(errs, msg)
|
||||
log.Error(msg)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !recursive {
|
||||
return errs
|
||||
}
|
||||
|
||||
dirs, err := c.Directories(from)
|
||||
|
||||
for _, dir := range dirs {
|
||||
errs = append(errs, c.DownloadDir(dir, to, true)...)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Upload uploads a single file to the remote server.
|
||||
func (c Client) Upload(from, to string) error {
|
||||
file, err := os.Open(from)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
return c.client.WriteStream(to, file, 0644)
|
||||
}
|
||||
|
||||
// Delete deletes a single file or directory on a remote server.
|
||||
func (c Client) Delete(path string) error {
|
||||
return c.client.Remove(path)
|
||||
}
|
115
internal/share/webdav/webdav_test.go
Normal file
115
internal/share/webdav/webdav_test.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package webdav
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
testUrl = "http://webdav-dummy/"
|
||||
testUser = "admin"
|
||||
testPass = "photoprism"
|
||||
)
|
||||
|
||||
func TestConnect(t *testing.T) {
|
||||
c := Connect(testUrl, testUser, testPass)
|
||||
|
||||
assert.IsType(t, Client{}, c)
|
||||
}
|
||||
|
||||
func TestClient_Files(t *testing.T) {
|
||||
c := Connect(testUrl, testUser, testPass)
|
||||
|
||||
assert.IsType(t, Client{}, c)
|
||||
|
||||
files, err := c.Files("Photos")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
t.Fatal("no files found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Download(t *testing.T) {
|
||||
c := Connect(testUrl, testUser, testPass)
|
||||
|
||||
assert.IsType(t, Client{}, c)
|
||||
|
||||
files, err := c.Files("Photos")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tempDir := os.TempDir() + rnd.UUID()
|
||||
tempFile := tempDir + "/foo.jpg"
|
||||
|
||||
if len(files) == 0 {
|
||||
t.Fatal("no files to download")
|
||||
}
|
||||
|
||||
if err := c.Download(files[0], tempFile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !fs.FileExists(tempFile) {
|
||||
t.Fatalf("%s does not exist", tempFile)
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(tempDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_DownloadDir(t *testing.T) {
|
||||
c := Connect(testUrl, testUser, testPass)
|
||||
|
||||
assert.IsType(t, Client{}, c)
|
||||
|
||||
t.Run("non-recursive", func(t *testing.T) {
|
||||
tempDir := os.TempDir() + rnd.UUID()
|
||||
|
||||
if errs := c.DownloadDir("Photos", tempDir, false); len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(tempDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("recursive", func(t *testing.T) {
|
||||
tempDir := os.TempDir() + rnd.UUID()
|
||||
|
||||
if errs := c.DownloadDir("Photos", tempDir, true); len(errs) > 0 {
|
||||
t.Fatal(errs)
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(tempDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestClient_UploadAndDelete(t *testing.T) {
|
||||
c := Connect(testUrl, testUser, testPass)
|
||||
|
||||
assert.IsType(t, Client{}, c)
|
||||
|
||||
tempName := rnd.UUID() + ".jpg"
|
||||
|
||||
if err := c.Upload("testdata/example.jpg", tempName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c.Delete(tempName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue