mirror of
https://github.com/documize/community.git
synced 2025-07-19 05:09:42 +02:00
[WIP] Restore process
Co-Authored-By: Harvey Kandola <harvey@documize.com>
This commit is contained in:
parent
e0457b40da
commit
71a2860716
7 changed files with 281 additions and 22 deletions
|
@ -32,11 +32,15 @@ package backup
|
||||||
// operations. This is subject to further review.
|
// operations. This is subject to further review.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/documize/community/core/request"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/documize/community/core/env"
|
"github.com/documize/community/core/env"
|
||||||
"github.com/documize/community/core/response"
|
"github.com/documize/community/core/response"
|
||||||
|
@ -104,10 +108,13 @@ func (h *Handler) Backup(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.Runtime.Log.Info(fmt.Sprintf("Backup size pending download %d", len(bk)))
|
||||||
|
|
||||||
// Standard HTTP headers.
|
// Standard HTTP headers.
|
||||||
w.Header().Set("Content-Type", "application/zip")
|
w.Header().Set("Content-Type", "application/zip")
|
||||||
w.Header().Set("Content-Disposition", `attachment; filename="`+filename+`" ; `+`filename*="`+filename+`"`)
|
w.Header().Set("Content-Disposition", `attachment; filename="`+filename+`" ; `+`filename*="`+filename+`"`)
|
||||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(bk)))
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(bk)))
|
||||||
|
|
||||||
// Custom HTTP header helps API consumer to extract backup filename cleanly
|
// Custom HTTP header helps API consumer to extract backup filename cleanly
|
||||||
// instead of parsing 'Content-Disposition' header.
|
// instead of parsing 'Content-Disposition' header.
|
||||||
// This HTTP header is CORS white-listed.
|
// This HTTP header is CORS white-listed.
|
||||||
|
@ -129,3 +136,51 @@ func (h *Handler) Backup(w http.ResponseWriter, r *http.Request) {
|
||||||
os.Remove(filename)
|
os.Remove(filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore receives ZIP file for restore operation.
|
||||||
|
// Options are specified as HTTP query paramaters.
|
||||||
|
func (h *Handler) Restore(w http.ResponseWriter, r *http.Request) {
|
||||||
|
method := "system.restore"
|
||||||
|
ctx := domain.GetRequestContext(r)
|
||||||
|
|
||||||
|
if !ctx.Administrator {
|
||||||
|
response.WriteForbiddenError(w)
|
||||||
|
h.Runtime.Log.Info(fmt.Sprintf("Non-admin attempted system restore operation (user ID: %s)", ctx.UserID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Runtime.Log.Info(fmt.Sprintf("Restored attempted by user: %s", ctx.UserID))
|
||||||
|
|
||||||
|
overwriteOrg, err := strconv.ParseBool(request.Query(r, "org"))
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Info("Restore invoked without 'org' parameter")
|
||||||
|
response.WriteMissingDataError(w, method, "org=false/true missing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
createUsers, err := strconv.ParseBool(request.Query(r, "users"))
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Info("Restore invoked without 'users' parameter")
|
||||||
|
response.WriteMissingDataError(w, method, "users=false/true missing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filedata, fileheader, err := r.FormFile("restore-file")
|
||||||
|
if err != nil {
|
||||||
|
response.WriteMissingDataError(w, method, "restore-file")
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
_, err = io.Copy(b, filedata)
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Runtime.Log.Info(fmt.Sprintf("%s %d %v %v", fileheader.Filename, len(b.Bytes()), overwriteOrg, createUsers))
|
||||||
|
|
||||||
|
response.WriteEmpty(w)
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Notifier from '../../mixins/notifier';
|
import Notifier from '../../mixins/notifier';
|
||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
|
@ -16,20 +17,40 @@ import Component from '@ember/component';
|
||||||
export default Component.extend(Notifier, {
|
export default Component.extend(Notifier, {
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
browserSvc: service('browser'),
|
browserSvc: service('browser'),
|
||||||
buttonLabel: 'Run Backup',
|
buttonLabel: 'Start Backup',
|
||||||
backupSpec: null,
|
backupSpec: null,
|
||||||
backupFilename: '',
|
backupFilename: '',
|
||||||
backupError: false,
|
backupError: false,
|
||||||
backupSuccess: false,
|
backupSuccess: false,
|
||||||
|
restoreSpec: null,
|
||||||
|
restoreButtonLabel: 'Perform Restore',
|
||||||
|
restoreUploading: false,
|
||||||
|
restoreUploadReady: false,
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
this.set('backupSpec', {
|
this.set('backupSpec', {
|
||||||
retain: false,
|
retain: true,
|
||||||
// org: '*'
|
|
||||||
org: this.get('appMeta.orgId')
|
org: this.get('appMeta.orgId')
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
this.set('restoreSpec', {
|
||||||
|
overwriteOrg: true,
|
||||||
|
recreateUsers: true
|
||||||
|
});
|
||||||
|
|
||||||
|
this.set('restoreFile', null);
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
this.$('#restore-file').on('change', function(){
|
||||||
|
var fileName = document.getElementById("restore-file").files[0].name;
|
||||||
|
$(this).next('.custom-file-label').html(fileName);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onBackup() {
|
onBackup() {
|
||||||
|
@ -37,17 +58,91 @@ export default Component.extend(Notifier, {
|
||||||
this.set('buttonLabel', 'Please wait, backup running...');
|
this.set('buttonLabel', 'Please wait, backup running...');
|
||||||
this.set('backupFilename', '');
|
this.set('backupFilename', '');
|
||||||
this.set('backupSuccess', false);
|
this.set('backupSuccess', false);
|
||||||
this.set('backupFailed', false);
|
this.set('backupFailed', false);
|
||||||
|
|
||||||
this.get('onBackup')(this.get('backupSpec')).then((filename) => {
|
// If Documize Global Admin we perform system-level backup.
|
||||||
this.set('buttonLabel', 'Run Backup');
|
// Otherwise it is current tenant backup.
|
||||||
|
let spec = this.get('backupSpec');
|
||||||
|
if (this.get('session.isGlobalAdmin')) {
|
||||||
|
spec.org = "*";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.get('onBackup')(spec).then((filename) => {
|
||||||
this.showDone();
|
this.showDone();
|
||||||
|
this.set('buttonLabel', 'Start Backup');
|
||||||
this.set('backupSuccess', true);
|
this.set('backupSuccess', true);
|
||||||
this.set('backupFilename', filename);
|
this.set('backupFilename', filename);
|
||||||
}, ()=> {
|
}, ()=> {
|
||||||
|
this.showDone();
|
||||||
this.set('buttonLabel', 'Run Backup');
|
this.set('buttonLabel', 'Run Backup');
|
||||||
this.set('backupFailed', true);
|
this.set('backupFailed', true);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onRestore() {
|
||||||
|
// do we have upload file?
|
||||||
|
// let files = document.getElementById("restore-file").files;
|
||||||
|
// if (is.undefined(files) || is.null(files)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let file = document.getElementById("restore-file").files[0];
|
||||||
|
// if (is.undefined(file) || is.null(file)) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
let filedata = this.get('restoreFile');
|
||||||
|
if (is.null(filedata)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start restore process
|
||||||
|
this.showWait();
|
||||||
|
this.set('restoreButtonLabel', 'Please wait, restore running...');
|
||||||
|
this.set('restoreSuccess', false);
|
||||||
|
this.set('restoreFailed', false);
|
||||||
|
|
||||||
|
// If Documize Global Admin we perform system-level restore.
|
||||||
|
// Otherwise it is current tenant backup.
|
||||||
|
let spec = this.get('restoreSpec');
|
||||||
|
if (this.get('session.isGlobalAdmin')) {
|
||||||
|
spec.org = "*";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.get('onRestore')(spec, filedata).then(() => {
|
||||||
|
this.showDone();
|
||||||
|
this.set('buttonLabel', 'Perform Restore');
|
||||||
|
this.set('restoreSuccess', true);
|
||||||
|
}, ()=> {
|
||||||
|
this.showDone();
|
||||||
|
this.set('restorButtonLabel', 'Perform Restore');
|
||||||
|
this.set('restoreFailed', true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
upload(event) {
|
||||||
|
this.set('restoreUploading', true);
|
||||||
|
this.set('restoreUploadReady', false);
|
||||||
|
this.set('restoreFile', null);
|
||||||
|
|
||||||
|
// const reader = new FileReader();
|
||||||
|
const file = event.target.files[0];
|
||||||
|
|
||||||
|
this.set('restoreFile', file);
|
||||||
|
this.set('restoreUploadReady', true);
|
||||||
|
this.set('restoreUploading', false);
|
||||||
|
|
||||||
|
// let imageData;
|
||||||
|
// reader.onload = () => {
|
||||||
|
// imageData = reader.result;
|
||||||
|
// this.set('restoreFile', imageData);
|
||||||
|
// this.set('restoreUploadReady', true);
|
||||||
|
// this.set('restoreUploading', false);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if (file) {
|
||||||
|
// reader.readAsDataURL(file);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,6 +18,7 @@ export default Service.extend({
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
browserSvc: service('browser'),
|
browserSvc: service('browser'),
|
||||||
store: service(),
|
store: service(),
|
||||||
|
router: service(),
|
||||||
|
|
||||||
// Returns SMTP configuration.
|
// Returns SMTP configuration.
|
||||||
getSMTPConfig() {
|
getSMTPConfig() {
|
||||||
|
@ -141,8 +142,12 @@ export default Service.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Run tenant level backup.
|
// Run backup.
|
||||||
backup(spec) {
|
backup(spec) {
|
||||||
|
if (!this.get('sessionService.isGlobalAdmin') || this.get('sessionService.isAdmin')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return new EmberPromise((resolve, reject) => {
|
return new EmberPromise((resolve, reject) => {
|
||||||
let url = this.get('appMeta.endpoint');
|
let url = this.get('appMeta.endpoint');
|
||||||
let token = this.get('sessionService.session.content.authenticated.token');
|
let token = this.get('sessionService.session.content.authenticated.token');
|
||||||
|
@ -184,5 +189,33 @@ export default Service.extend({
|
||||||
|
|
||||||
xhr.send(JSON.stringify(spec));
|
xhr.send(JSON.stringify(spec));
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
restore(spec, file) {
|
||||||
|
var data = new FormData();
|
||||||
|
data.set('restore-file', file);
|
||||||
|
|
||||||
|
return new EmberPromise((resolve, reject) => {
|
||||||
|
let url = this.get('appMeta.endpoint');
|
||||||
|
let token = this.get('sessionService.session.content.authenticated.token');
|
||||||
|
let uploadUrl = `${url}/global/restore?token=${token}&org=${spec.overwriteOrg}&users=${spec.recreateUsers}`;
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', uploadUrl);
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (this.status == 200) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.onerror= function() {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.send(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
1
gui/app/styles/bootstrap.scss
vendored
1
gui/app/styles/bootstrap.scss
vendored
|
@ -112,6 +112,7 @@ $link-hover-decoration: none;
|
||||||
@import "node_modules/bootstrap/scss/button-group";
|
@import "node_modules/bootstrap/scss/button-group";
|
||||||
@import "node_modules/bootstrap/scss/dropdown";
|
@import "node_modules/bootstrap/scss/dropdown";
|
||||||
@import "node_modules/bootstrap/scss/forms";
|
@import "node_modules/bootstrap/scss/forms";
|
||||||
|
@import "node_modules/bootstrap/scss/custom-forms";
|
||||||
@import "node_modules/bootstrap/scss/input-group";
|
@import "node_modules/bootstrap/scss/input-group";
|
||||||
@import "node_modules/bootstrap/scss/modal";
|
@import "node_modules/bootstrap/scss/modal";
|
||||||
@import "node_modules/bootstrap/scss/utilities";
|
@import "node_modules/bootstrap/scss/utilities";
|
||||||
|
|
|
@ -152,4 +152,78 @@
|
||||||
> .max-results {
|
> .max-results {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .backup-restore {
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
|
||||||
|
> .backup-zone {
|
||||||
|
@include border-radius(3px);
|
||||||
|
border: 1px solid $color-border;
|
||||||
|
padding: 20px 20px;
|
||||||
|
background-color: lighten($color-green, 60%);
|
||||||
|
|
||||||
|
> .explain {
|
||||||
|
color: $color-gray;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .backup-fail {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: $color-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .backup-success {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: $color-green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .restore-zone {
|
||||||
|
@include border-radius(3px);
|
||||||
|
border: 1px solid $color-border;
|
||||||
|
margin: 50px 0;
|
||||||
|
padding: 20px 20px;
|
||||||
|
background-color: lighten($color-red, 60%);
|
||||||
|
|
||||||
|
> .restore-fail {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: $color-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .restore-success {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: $color-green;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .upload-backup-file {
|
||||||
|
@include ease-in();
|
||||||
|
margin: 50px 0 10px 0;
|
||||||
|
|
||||||
|
> .dz-preview, .dz-processing {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.restore-upload-busy {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .wait {
|
||||||
|
color: $color-gray;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .ready {
|
||||||
|
color: $color-green;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,33 +7,33 @@
|
||||||
margin: 0 0 5px 0;
|
margin: 0 0 5px 0;
|
||||||
|
|
||||||
> .material-icons {
|
> .material-icons {
|
||||||
font-size: 1rem;
|
font-size: 1.5rem;
|
||||||
color: $color-gray;
|
color: $color-gray;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .selected {
|
> .selected {
|
||||||
color: $color-link;
|
color: $color-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $color-link;
|
color: $color-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .text {
|
> .text {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 0.9rem;
|
font-size: 1.1rem;
|
||||||
vertical-align: text-top;
|
vertical-align: sub;
|
||||||
color: $color-off-black;
|
color: $color-off-black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-checkbox-selected {
|
.ui-checkbox-selected {
|
||||||
color: $color-link;
|
color: $color-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget-checkbox {
|
.widget-checkbox {
|
||||||
color: $color-link;
|
color: $color-blue;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ func RegisterEndpoints(rt *env.Runtime, s *store.Store) {
|
||||||
// Secured private routes (require authentication)
|
// Secured private routes (require authentication)
|
||||||
//**************************************************
|
//**************************************************
|
||||||
|
|
||||||
AddPrivate(rt, "import/folder/{folderID}", []string{"POST", "OPTIONS"}, nil, conversion.UploadConvert)
|
AddPrivate(rt, "import/folder/{spaceID}", []string{"POST", "OPTIONS"}, nil, conversion.UploadConvert)
|
||||||
|
|
||||||
AddPrivate(rt, "documents", []string{"GET", "OPTIONS"}, nil, document.BySpace)
|
AddPrivate(rt, "documents", []string{"GET", "OPTIONS"}, nil, document.BySpace)
|
||||||
AddPrivate(rt, "documents/{documentID}", []string{"GET", "OPTIONS"}, nil, document.Get)
|
AddPrivate(rt, "documents/{documentID}", []string{"GET", "OPTIONS"}, nil, document.Get)
|
||||||
|
@ -161,19 +161,19 @@ func RegisterEndpoints(rt *env.Runtime, s *store.Store) {
|
||||||
AddPrivate(rt, "search", []string{"POST", "OPTIONS"}, nil, document.SearchDocuments)
|
AddPrivate(rt, "search", []string{"POST", "OPTIONS"}, nil, document.SearchDocuments)
|
||||||
|
|
||||||
AddPrivate(rt, "templates", []string{"POST", "OPTIONS"}, nil, template.SaveAs)
|
AddPrivate(rt, "templates", []string{"POST", "OPTIONS"}, nil, template.SaveAs)
|
||||||
AddPrivate(rt, "templates/{templateID}/folder/{folderID}", []string{"POST", "OPTIONS"}, []string{"type", "saved"}, template.Use)
|
AddPrivate(rt, "templates/{templateID}/folder/{spaceID}", []string{"POST", "OPTIONS"}, []string{"type", "saved"}, template.Use)
|
||||||
AddPrivate(rt, "templates/{folderID}", []string{"GET", "OPTIONS"}, nil, template.SavedList)
|
AddPrivate(rt, "templates/{spaceID}", []string{"GET", "OPTIONS"}, nil, template.SavedList)
|
||||||
|
|
||||||
AddPrivate(rt, "sections", []string{"GET", "OPTIONS"}, nil, section.GetSections)
|
AddPrivate(rt, "sections", []string{"GET", "OPTIONS"}, nil, section.GetSections)
|
||||||
AddPrivate(rt, "sections", []string{"POST", "OPTIONS"}, nil, section.RunSectionCommand)
|
AddPrivate(rt, "sections", []string{"POST", "OPTIONS"}, nil, section.RunSectionCommand)
|
||||||
AddPrivate(rt, "sections/refresh", []string{"GET", "OPTIONS"}, nil, section.RefreshSections)
|
AddPrivate(rt, "sections/refresh", []string{"GET", "OPTIONS"}, nil, section.RefreshSections)
|
||||||
AddPrivate(rt, "sections/blocks/space/{folderID}", []string{"GET", "OPTIONS"}, nil, block.GetBySpace)
|
AddPrivate(rt, "sections/blocks/space/{spaceID}", []string{"GET", "OPTIONS"}, nil, block.GetBySpace)
|
||||||
AddPrivate(rt, "sections/blocks/{blockID}", []string{"GET", "OPTIONS"}, nil, block.Get)
|
AddPrivate(rt, "sections/blocks/{blockID}", []string{"GET", "OPTIONS"}, nil, block.Get)
|
||||||
AddPrivate(rt, "sections/blocks/{blockID}", []string{"PUT", "OPTIONS"}, nil, block.Update)
|
AddPrivate(rt, "sections/blocks/{blockID}", []string{"PUT", "OPTIONS"}, nil, block.Update)
|
||||||
AddPrivate(rt, "sections/blocks/{blockID}", []string{"DELETE", "OPTIONS"}, nil, block.Delete)
|
AddPrivate(rt, "sections/blocks/{blockID}", []string{"DELETE", "OPTIONS"}, nil, block.Delete)
|
||||||
AddPrivate(rt, "sections/blocks", []string{"POST", "OPTIONS"}, nil, block.Add)
|
AddPrivate(rt, "sections/blocks", []string{"POST", "OPTIONS"}, nil, block.Add)
|
||||||
|
|
||||||
AddPrivate(rt, "links/{folderID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, link.GetLinkCandidates)
|
AddPrivate(rt, "links/{spaceID}/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, link.GetLinkCandidates)
|
||||||
AddPrivate(rt, "links", []string{"GET", "OPTIONS"}, nil, link.SearchLinkCandidates)
|
AddPrivate(rt, "links", []string{"GET", "OPTIONS"}, nil, link.SearchLinkCandidates)
|
||||||
AddPrivate(rt, "documents/{documentID}/links", []string{"GET", "OPTIONS"}, nil, document.DocumentLinks)
|
AddPrivate(rt, "documents/{documentID}/links", []string{"GET", "OPTIONS"}, nil, document.DocumentLinks)
|
||||||
|
|
||||||
|
@ -220,6 +220,7 @@ func RegisterEndpoints(rt *env.Runtime, s *store.Store) {
|
||||||
AddPrivate(rt, "global/ldap/preview", []string{"POST", "OPTIONS"}, nil, ldap.Preview)
|
AddPrivate(rt, "global/ldap/preview", []string{"POST", "OPTIONS"}, nil, ldap.Preview)
|
||||||
AddPrivate(rt, "global/ldap/sync", []string{"GET", "OPTIONS"}, nil, ldap.Sync)
|
AddPrivate(rt, "global/ldap/sync", []string{"GET", "OPTIONS"}, nil, ldap.Sync)
|
||||||
AddPrivate(rt, "global/backup", []string{"POST", "OPTIONS"}, nil, backup.Backup)
|
AddPrivate(rt, "global/backup", []string{"POST", "OPTIONS"}, nil, backup.Backup)
|
||||||
|
AddPrivate(rt, "global/restore", []string{"POST", "OPTIONS"}, nil, backup.Restore)
|
||||||
|
|
||||||
Add(rt, RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, meta.RobotsTxt)
|
Add(rt, RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, meta.RobotsTxt)
|
||||||
Add(rt, RoutePrefixRoot, "sitemap.xml", []string{"GET", "OPTIONS"}, nil, meta.Sitemap)
|
Add(rt, RoutePrefixRoot, "sitemap.xml", []string{"GET", "OPTIONS"}, nil, meta.Sitemap)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue