Add event hub & websocket for push notifications
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
9b03cc4d6d
commit
65f084193e
94
frontend/package-lock.json
generated
94
frontend/package-lock.json
generated
|
@ -4404,39 +4404,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"engine.io-client": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
|
||||
"integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
|
||||
"requires": {
|
||||
"component-emitter": "1.2.1",
|
||||
"component-inherit": "0.0.3",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-parser": "~2.1.1",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.5",
|
||||
"parseuri": "0.0.5",
|
||||
"ws": "~3.3.1",
|
||||
"xmlhttprequest-ssl": "~1.5.4",
|
||||
"yeast": "0.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"component-emitter": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
|
||||
|
@ -6171,9 +6138,9 @@
|
|||
}
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
|
||||
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.2.tgz",
|
||||
"integrity": "sha512-29Zxv/cynYB7mkT1rVWQnV7mGX6v7H/miQ6dbEpYTKq5eJBN7PsRB+ViYJlcT6JINTSu4dVB9kOqEun78h6Exg==",
|
||||
"requires": {
|
||||
"neo-async": "^2.6.0",
|
||||
"optimist": "^0.6.1",
|
||||
|
@ -10714,6 +10681,11 @@
|
|||
"socket.io-parser": "~3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"component-emitter": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
|
@ -10721,13 +10693,24 @@
|
|||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-adapter": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
|
||||
"integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
|
||||
"engine.io-client": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
|
||||
"integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
|
||||
"requires": {
|
||||
"component-emitter": "1.2.1",
|
||||
"component-inherit": "0.0.3",
|
||||
"debug": "~3.1.0",
|
||||
"engine.io-parser": "~2.1.1",
|
||||
"has-cors": "1.1.0",
|
||||
"indexof": "0.0.1",
|
||||
"parseqs": "0.0.5",
|
||||
"parseuri": "0.0.5",
|
||||
"ws": "~3.3.1",
|
||||
"xmlhttprequest-ssl": "~1.5.4",
|
||||
"yeast": "0.1.2"
|
||||
}
|
||||
},
|
||||
"socket.io-client": {
|
||||
"version": "2.1.1",
|
||||
|
@ -10748,22 +10731,14 @@
|
|||
"parseuri": "0.0.5",
|
||||
"socket.io-parser": "~3.2.0",
|
||||
"to-array": "0.1.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"component-emitter": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
"socket.io-adapter": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
|
||||
"integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "3.2.0",
|
||||
|
@ -10795,6 +10770,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"sockette": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/sockette/-/sockette-2.0.6.tgz",
|
||||
"integrity": "sha512-W6iG8RGV6Zife3Cj+FhuyHV447E6fqFM2hKmnaQrTvg3OydINV3Msj3WPFbX76blUlUxvQSMMMdrJxce8NqI5Q=="
|
||||
},
|
||||
"sort-keys": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^7.3.1",
|
||||
"sinon": "^7.5.0",
|
||||
"sockette": "^2.0.6",
|
||||
"style-loader": "^0.23.1",
|
||||
"sugarss": "^2.0.0",
|
||||
"svg-url-loader": "^2.3.3",
|
||||
|
|
|
@ -4,6 +4,7 @@ import Router from "vue-router";
|
|||
import PhotoPrism from "photoprism.vue";
|
||||
import Routes from "routes";
|
||||
import Api from "common/api";
|
||||
import Socket from "common/websocket";
|
||||
import Config from "common/config";
|
||||
import Clipboard from "common/clipboard";
|
||||
import Components from "component/components";
|
||||
|
@ -33,6 +34,7 @@ Vue.prototype.$alert = Alert;
|
|||
Vue.prototype.$viewer = viewer;
|
||||
Vue.prototype.$session = Session;
|
||||
Vue.prototype.$api = Api;
|
||||
Vue.prototype.$socket = Socket;
|
||||
Vue.prototype.$config = config;
|
||||
Vue.prototype.$clipboard = clipboard;
|
||||
|
||||
|
|
|
@ -2,16 +2,16 @@ import Event from "pubsub-js";
|
|||
|
||||
const Alert = {
|
||||
info: function (message) {
|
||||
Event.publish("alert.info", message);
|
||||
Event.publish("alert.info", {msg: message});
|
||||
},
|
||||
warning: function (message) {
|
||||
Event.publish("alert.warning", message);
|
||||
Event.publish("alert.warning", {msg: message});
|
||||
},
|
||||
error: function (message) {
|
||||
Event.publish("alert.error", message);
|
||||
Event.publish("alert.error", {msg: message});
|
||||
},
|
||||
success: function (message) {
|
||||
Event.publish("alert.success", message);
|
||||
Event.publish("alert.success", {msg: message});
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import "@babel/polyfill/noConflict";
|
||||
import axios from "axios";
|
||||
import Event from "pubsub-js";
|
||||
import "@babel/polyfill/noConflict";
|
||||
import Alert from "common/alert";
|
||||
|
||||
const Api = axios.create({
|
||||
baseURL: "/api/v1",
|
||||
|
@ -36,7 +37,7 @@ Api.interceptors.response.use(function (response) {
|
|||
}
|
||||
|
||||
Event.publish("ajax.end");
|
||||
Event.publish("alert.error", errorMessage);
|
||||
Alert.error(errorMessage);
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
|
22
frontend/src/common/websocket.js
Normal file
22
frontend/src/common/websocket.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import Sockette from "sockette";
|
||||
import Event from "pubsub-js";
|
||||
|
||||
const host = window.location.host;
|
||||
const Socket = new Sockette("ws://" + host + "/api/v1/ws", {
|
||||
timeout: 5e3,
|
||||
onopen: e => {
|
||||
console.log('Connected!', e);
|
||||
Socket.send("hello world");
|
||||
},
|
||||
onmessage: e => {
|
||||
const m = JSON.parse(e.data);
|
||||
console.log('Received:', m);
|
||||
Event.publish(m.event, m.data);
|
||||
},
|
||||
onreconnect: e => console.log('Reconnecting...', e),
|
||||
onmaximum: e => console.log('Stop Attempting!', e),
|
||||
onclose: e => console.log('Closed!', e),
|
||||
onerror: e => console.log('Error:', e)
|
||||
});
|
||||
|
||||
export default Socket;
|
|
@ -43,24 +43,30 @@
|
|||
Event.unsubscribe(this.subscriptionId);
|
||||
},
|
||||
methods: {
|
||||
handleAlertEvent: function (ev, message) {
|
||||
handleAlertEvent: function (ev, data) {
|
||||
const type = ev.split('.')[1];
|
||||
|
||||
// get message from data object
|
||||
let m = data.msg;
|
||||
|
||||
// first letter uppercase
|
||||
m = m.replace(/^./, m[0].toUpperCase());
|
||||
|
||||
switch (type) {
|
||||
case 'warning':
|
||||
this.addWarningMessage(message);
|
||||
this.addWarningMessage(m);
|
||||
break;
|
||||
case 'error':
|
||||
this.addErrorMessage(message);
|
||||
this.addErrorMessage(m);
|
||||
break;
|
||||
case 'success':
|
||||
this.addSuccessMessage(message);
|
||||
this.addSuccessMessage(m);
|
||||
break;
|
||||
case 'info':
|
||||
this.addInfoMessage(message);
|
||||
this.addInfoMessage(m);
|
||||
break;
|
||||
default:
|
||||
alert(message);
|
||||
alert(m);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
|
||||
Api.post("batch/photos/private", {"ids": this.selection}).then(function () {
|
||||
Event.publish("ajax.end");
|
||||
Event.publish("alert.success", "Toggled private flag");
|
||||
this.$alert.success("Toggled private flag");
|
||||
ctx.clearClipboard();
|
||||
ctx.refresh();
|
||||
}).catch(() => {
|
||||
|
@ -145,7 +145,7 @@
|
|||
|
||||
Api.post("batch/photos/story", {"ids": this.selection}).then(function () {
|
||||
Event.publish("ajax.end");
|
||||
Event.publish("alert.success", "Toggled story flag");
|
||||
this.$alert.success("Toggled story flag");
|
||||
ctx.clearClipboard();
|
||||
ctx.refresh();
|
||||
}).catch(() => {
|
||||
|
@ -161,7 +161,7 @@
|
|||
|
||||
Api.post("batch/photos/delete", {"ids": this.selection}).then(function () {
|
||||
Event.publish("ajax.end");
|
||||
Event.publish("alert.success", "Photos deleted");
|
||||
this.$alert.success("Photos deleted");
|
||||
ctx.clearClipboard();
|
||||
ctx.refresh();
|
||||
}).catch(() => {
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
<v-form ref="form" class="p-photo-import" lazy-validation @submit.prevent="submit" dense>
|
||||
<v-container fluid>
|
||||
<p class="subheading">
|
||||
<span v-if="busy">Importing files from directory...</span>
|
||||
<span v-if="fileName">Indexed {{ fileName}}...</span>
|
||||
<span v-else-if="busy">Importing files from directory...</span>
|
||||
<span v-else-if="completed">Done.</span>
|
||||
<span v-else>Press button to import photos from directory...</span>
|
||||
</p>
|
||||
|
@ -26,7 +27,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "common/api";
|
||||
import Event from "pubsub-js";
|
||||
|
||||
export default {
|
||||
|
@ -36,6 +36,8 @@
|
|||
started: false,
|
||||
busy: false,
|
||||
completed: 0,
|
||||
subscriptionId: '',
|
||||
fileName: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -46,21 +48,45 @@
|
|||
this.started = Date.now();
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
|
||||
this.$alert.info("Importing photos...");
|
||||
this.fileName = '';
|
||||
|
||||
const ctx = this;
|
||||
|
||||
Api.post('import').then(function () {
|
||||
Event.publish("alert.success", "Import complete");
|
||||
this.$api.post('import').then(function () {
|
||||
ctx.busy = false;
|
||||
ctx.completed = 100;
|
||||
this.fileName = '';
|
||||
}).catch(function () {
|
||||
Event.publish("alert.error", "Import failed");
|
||||
this.$alert.error("Import failed");
|
||||
ctx.busy = false;
|
||||
ctx.completed = 0;
|
||||
this.fileName = '';
|
||||
});
|
||||
},
|
||||
handleEvent(ev, data) {
|
||||
const type = ev.split('.')[1];
|
||||
|
||||
switch (type) {
|
||||
case 'file':
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
this.fileName = data.fileName;
|
||||
break;
|
||||
case 'completed':
|
||||
this.busy = false;
|
||||
this.completed = 100;
|
||||
this.fileName = '';
|
||||
break;
|
||||
default:
|
||||
console.log(data)
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.subscriptionId = Event.subscribe('import', this.handleEvent);
|
||||
},
|
||||
destroyed() {
|
||||
Event.unsubscribe(this.subscriptionId);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
<v-form ref="form" class="p-photo-index" lazy-validation @submit.prevent="submit" dense>
|
||||
<v-container fluid>
|
||||
<p class="subheading">
|
||||
<span v-if="busy">Re-indexing existing files and photos...</span>
|
||||
<span v-if="fileName">Indexed {{ fileName}}...</span>
|
||||
<span v-else-if="busy">Re-indexing existing files and photos...</span>
|
||||
<span v-else-if="completed">Done.</span>
|
||||
<span v-else>Press button to re-index existing files and photos...</span>
|
||||
</p>
|
||||
|
@ -26,7 +27,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "common/api";
|
||||
import Event from "pubsub-js";
|
||||
|
||||
export default {
|
||||
|
@ -36,6 +36,8 @@
|
|||
started: false,
|
||||
busy: false,
|
||||
completed: 0,
|
||||
subscriptionId: '',
|
||||
fileName: '',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -46,21 +48,45 @@
|
|||
this.started = Date.now();
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
|
||||
this.$alert.info("Indexing photos...");
|
||||
this.fileName = '';
|
||||
|
||||
const ctx = this;
|
||||
|
||||
Api.post('index').then(function () {
|
||||
Event.publish("alert.success", "Indexing complete");
|
||||
this.$api.post('index').then(function () {
|
||||
ctx.busy = false;
|
||||
ctx.completed = 100;
|
||||
this.fileName = '';
|
||||
}).catch(function () {
|
||||
Event.publish("alert.error", "Indexing failed");
|
||||
this.$alert.error("Indexing failed");
|
||||
ctx.busy = false;
|
||||
ctx.completed = 0;
|
||||
this.fileName = '';
|
||||
});
|
||||
},
|
||||
handleEvent(ev, data) {
|
||||
const type = ev.split('.')[1];
|
||||
|
||||
switch (type) {
|
||||
case 'file':
|
||||
this.busy = true;
|
||||
this.completed = 0;
|
||||
this.fileName = data.fileName;
|
||||
break;
|
||||
case 'completed':
|
||||
this.busy = false;
|
||||
this.completed = 100;
|
||||
this.fileName = '';
|
||||
break;
|
||||
default:
|
||||
console.log(data)
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.subscriptionId = Event.subscribe('index', this.handleEvent);
|
||||
},
|
||||
destroyed() {
|
||||
Event.unsubscribe(this.subscriptionId);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "common/api";
|
||||
import Event from "pubsub-js";
|
||||
|
||||
export default {
|
||||
|
@ -80,7 +79,7 @@
|
|||
|
||||
formData.append('files', file);
|
||||
|
||||
await Api.post('upload/' + ctx.started,
|
||||
await this.$api.post('upload/' + ctx.started,
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
|
@ -90,7 +89,7 @@
|
|||
).then(function () {
|
||||
ctx.completed = Math.round((ctx.current / ctx.total) * 100);
|
||||
}).catch(function () {
|
||||
Event.publish("alert.error", "Upload failed");
|
||||
this.$alert.error("Upload failed");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -99,12 +98,12 @@
|
|||
this.indexing = true;
|
||||
const ctx = this;
|
||||
|
||||
Api.post('import/upload/' + this.started).then(function () {
|
||||
Event.publish("alert.success", "Upload complete");
|
||||
this.$api.post('import/upload/' + this.started).then(function () {
|
||||
this.$alert.success("Upload complete");
|
||||
ctx.busy = false;
|
||||
ctx.indexing = false;
|
||||
}).catch(function () {
|
||||
Event.publish("alert.error", "Failure while importing uploaded files");
|
||||
this.$alert.error("Failure while importing uploaded files");
|
||||
ctx.busy = false;
|
||||
ctx.indexing = false;
|
||||
});
|
||||
|
|
10
go.mod
10
go.mod
|
@ -13,12 +13,14 @@ require (
|
|||
github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f
|
||||
github.com/dsoprea/go-logging v0.0.0-20190409182557-13b4fff49234 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.3.0
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20190507233405-a0e886e97a51 // indirect
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/gorilla/websocket v1.4.0 // indirect
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/gosimple/slug v1.5.0
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect
|
||||
|
@ -26,6 +28,7 @@ require (
|
|||
github.com/json-iterator/go v1.1.5 // indirect
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/leandro-lugaresi/hub v1.1.0
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
|
@ -60,11 +63,12 @@ require (
|
|||
github.com/ugorji/go v1.1.7 // indirect
|
||||
github.com/unrolled/render v0.0.0-20181210145518-4c664cb3ad2f // indirect
|
||||
github.com/urfave/cli v1.20.0
|
||||
go.uber.org/atomic v1.4.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d // indirect
|
||||
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b // indirect
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
golang.org/x/text v0.3.1 // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
|
||||
|
|
20
go.sum
20
go.sum
|
@ -79,8 +79,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
|||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
|
||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
|
@ -106,6 +106,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
|||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
|
@ -115,6 +117,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCy
|
|||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
|
@ -125,8 +129,8 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
|
|||
github.com/gorilla/mux v0.0.0-20170228224354-599cba5e7b61/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gosimple/slug v1.5.0 h1:AIIjgCjHcLpX8LzM2NpG4QGW9kUfqv0OLiFRfPv/H3E=
|
||||
github.com/gosimple/slug v1.5.0/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20171020063731-82921fcf811d/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
|
@ -163,6 +167,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leandro-lugaresi/hub v1.1.0 h1:yHYA0WsMYaJd+I6J24nYlCP2CFD4RTnhaHCRmKjv3q4=
|
||||
github.com/leandro-lugaresi/hub v1.1.0/go.mod h1:IVKrfZTYfU1SbWCGQMHNGYdW4j1Pl7Cg8gr6sSeT/84=
|
||||
github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
|
||||
|
@ -313,6 +319,8 @@ github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Y
|
|||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o=
|
||||
|
@ -347,8 +355,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTm
|
|||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -366,6 +372,8 @@ golang.org/x/text v0.3.1/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181105230042-78dc5bac0cac h1:0Nb35Izc6T6Yz1iGmRc4cg14cxRaFjbjD4hWFI6JNJ8=
|
||||
|
|
|
@ -3,9 +3,11 @@ package api
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
|
@ -46,14 +48,17 @@ func Import(router *gin.RouterGroup, conf *config.Config) {
|
|||
path = path + subPath
|
||||
}
|
||||
|
||||
log.Infof("importing photos from %s", path)
|
||||
event.Info(fmt.Sprintf("importing photos from \"%s\"", filepath.Base(path)))
|
||||
|
||||
initImporter(conf)
|
||||
|
||||
importer.ImportPhotosFromDirectory(path)
|
||||
|
||||
elapsed := time.Since(start)
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("completed import in %s", elapsed)})
|
||||
event.Success(fmt.Sprintf("completed import in %d s", elapsed))
|
||||
event.Publish("import.completed", event.Data{"path": path, "seconds": elapsed})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("completed import in %d s", elapsed)})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ package api
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
)
|
||||
|
||||
|
@ -33,14 +35,17 @@ func Index(router *gin.RouterGroup, conf *config.Config) {
|
|||
start := time.Now()
|
||||
path := conf.OriginalsPath()
|
||||
|
||||
log.Infof("indexing photos in %s", path)
|
||||
event.Info(fmt.Sprintf("indexing photos in \"%s\"", filepath.Base(path)))
|
||||
|
||||
initIndexer(conf)
|
||||
|
||||
indexer.IndexAll()
|
||||
|
||||
elapsed := time.Since(start)
|
||||
elapsed := int(time.Since(start).Seconds())
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("indexing completed in %s", elapsed)})
|
||||
event.Success(fmt.Sprintf("indexing completed in %d s", elapsed))
|
||||
event.Publish("index.completed", event.Data{"path": path, "seconds": elapsed})
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("indexing completed in %d s", elapsed)})
|
||||
})
|
||||
}
|
||||
|
|
79
internal/api/websocket.go
Normal file
79
internal/api/websocket.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
)
|
||||
|
||||
var wsConnection = websocket.Upgrader{}
|
||||
var wsTimeout = 60 * time.Second
|
||||
|
||||
func wsReader(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
|
||||
ws.SetReadLimit(512)
|
||||
ws.SetReadDeadline(time.Now().Add(wsTimeout))
|
||||
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(wsTimeout)); return nil })
|
||||
|
||||
for {
|
||||
_, m, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
log.Infof("received: %s", m)
|
||||
}
|
||||
}
|
||||
|
||||
func wsWriter(ws *websocket.Conn, conf *config.Config) {
|
||||
pingTicker := time.NewTicker(10 * time.Second)
|
||||
s := event.Subscribe("index.*", "alert.*")
|
||||
|
||||
defer func() {
|
||||
pingTicker.Stop()
|
||||
event.Unsubscribe(s)
|
||||
ws.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-pingTicker.C:
|
||||
ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
case msg := <-s.Receiver:
|
||||
ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
if err := ws.WriteJSON(gin.H{"event": msg.Name, "data": msg.Fields}); err != nil {
|
||||
log.Errorf("write json: %s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/v1/ws
|
||||
func Websocket(router *gin.RouterGroup, conf *config.Config) {
|
||||
router.GET("/ws", func(c *gin.Context) {
|
||||
w := c.Writer
|
||||
r := c.Request
|
||||
|
||||
ws, err := wsConnection.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Errorf("upgrade error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer ws.Close()
|
||||
|
||||
log.Infof("websocket connected: %s", c.Request.RemoteAddr)
|
||||
|
||||
go wsWriter(ws, conf)
|
||||
|
||||
wsReader(ws)
|
||||
})
|
||||
}
|
61
internal/event/hub.go
Normal file
61
internal/event/hub.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Hub = hub.Hub
|
||||
type Data = hub.Fields
|
||||
type Message = hub.Message
|
||||
var log *logrus.Logger
|
||||
var channelCap = 10
|
||||
var sharedHub = NewHub()
|
||||
|
||||
func init() {
|
||||
log = logrus.StandardLogger()
|
||||
}
|
||||
|
||||
func NewHub () *Hub {
|
||||
return hub.New()
|
||||
}
|
||||
|
||||
func SharedHub() *Hub {
|
||||
return sharedHub
|
||||
}
|
||||
|
||||
func Error(msg string) {
|
||||
log.Error(msg)
|
||||
Publish("alert.error", Data{"msg": msg})
|
||||
}
|
||||
|
||||
func Success(msg string) {
|
||||
log.Info(msg)
|
||||
Publish("alert.success", Data{"msg": msg})
|
||||
}
|
||||
|
||||
func Info(msg string) {
|
||||
log.Info(msg)
|
||||
Publish("alert.info", Data{"msg": msg})
|
||||
}
|
||||
|
||||
func Warning(msg string) {
|
||||
log.Warn(msg)
|
||||
Publish("alert.warning", Data{"msg": msg})
|
||||
}
|
||||
|
||||
func Publish (event string, data Data) {
|
||||
log.Infof("publish %s: %v", event, data)
|
||||
SharedHub().Publish(Message{
|
||||
Name: event,
|
||||
Fields: data,
|
||||
})
|
||||
}
|
||||
|
||||
func Subscribe(topics ...string) hub.Subscription {
|
||||
return SharedHub().Subscribe(channelCap, topics...)
|
||||
}
|
||||
|
||||
func Unsubscribe(s hub.Subscription) {
|
||||
SharedHub().Unsubscribe(s)
|
||||
}
|
31
internal/event/hub_test.go
Normal file
31
internal/event/hub_test.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/leandro-lugaresi/hub"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSharedHub(t *testing.T) {
|
||||
h := SharedHub()
|
||||
|
||||
assert.IsType(t, &hub.Hub{}, h)
|
||||
}
|
||||
|
||||
func TestPublishSubscribe(t *testing.T) {
|
||||
s := Subscribe("foo.bar")
|
||||
|
||||
assert.IsType(t, hub.Subscription{}, s)
|
||||
|
||||
Publish("foo.bar", Data{"id": 13})
|
||||
|
||||
msg := <-s.Receiver
|
||||
|
||||
t.Logf("receive msg with topic %s: %v\n", msg.Name, msg.Fields)
|
||||
|
||||
assert.Equal(t, "foo.bar", msg.Name)
|
||||
assert.Equal(t, Data{"id": 13}, msg.Fields)
|
||||
|
||||
Unsubscribe(s)
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/models"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/util"
|
||||
|
@ -78,11 +79,15 @@ func (i *Importer) ImportPhotosFromDirectory(importPath string) {
|
|||
relatedFiles, mainFile, err := mediaFile.RelatedFiles()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("could not import \"%s\": %s", mediaFile.RelativeFilename(importPath), err.Error())
|
||||
event.Error(fmt.Sprintf("could not import \"%s\": %s", mediaFile.RelativeFilename(importPath), err.Error()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
event.Publish("import.file", event.Data{
|
||||
"fileName": mainFile.Filename(),
|
||||
})
|
||||
|
||||
for _, relatedMediaFile := range relatedFiles {
|
||||
relativeFilename := relatedMediaFile.RelativeFilename(importPath)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/models"
|
||||
"github.com/photoprism/photoprism/internal/util"
|
||||
)
|
||||
|
@ -338,6 +339,17 @@ func (i *Indexer) indexMediaFile(mediaFile *MediaFile) string {
|
|||
file.FilePortrait = mediaFile.Width() < mediaFile.Height()
|
||||
}
|
||||
|
||||
event.Publish("index.file", event.Data{
|
||||
"photoID": file.PhotoID,
|
||||
"filePrimary": file.FilePrimary,
|
||||
"fileMissing": file.FileMissing,
|
||||
"fileName": file.FileName,
|
||||
"fileHash": file.FileHash,
|
||||
"fileType": file.FileType,
|
||||
"fileMime": file.FileMime,
|
||||
"updated": fileQuery.Error == nil,
|
||||
})
|
||||
|
||||
if fileQuery.Error == nil {
|
||||
i.db.Unscoped().Save(&file)
|
||||
return indexResultUpdated
|
||||
|
|
|
@ -15,6 +15,12 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
|||
// Static assets like js and css files
|
||||
router.Static("/static", conf.HttpStaticPath())
|
||||
|
||||
// socket.io
|
||||
/* s := router.Group("/socket.io")
|
||||
{
|
||||
api.Socket(s, conf)
|
||||
} */
|
||||
|
||||
// JSON-REST API Version 1
|
||||
v1 := router.Group("/api/v1")
|
||||
{
|
||||
|
@ -49,6 +55,8 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
|||
|
||||
api.GetSettings(v1, conf)
|
||||
api.SaveSettings(v1, conf)
|
||||
|
||||
api.Websocket(v1, conf)
|
||||
}
|
||||
|
||||
// Default HTML page (client-side routing implemented via Vue.js)
|
||||
|
|
Loading…
Reference in a new issue