yannstatic/static/2022/07/21/Matrix_Synapse_Home_Server.html

2150 lines
202 KiB
HTML
Raw Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>Matrix Synapse Home Server - YannStatic</title>
<meta name="description" content="Matrix Synapse Home Server">
<link rel="canonical" href="https://static.rnmkcy.eu/2022/07/21/Matrix_Synapse_Home_Server.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root"><!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main">
<div class="header__title">
<div class="header__brand"><svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewBox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.5
" href="/">YannStatic</a></div><!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">Matrix Synapse Home Server</h1></header></div><meta itemprop="headline" content="Matrix Synapse Home Server"><div class="article__info clearfix"><ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=vps">vps</a>
2024-10-31 20:18:37 +01:00
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">21&nbsp;juil.&nbsp;2022</span>
<span title="Modification" style="color:#00FF7F">27&nbsp;juil.&nbsp;2022</span></li></ul></div><meta itemprop="datePublished" content="2022-07-27T00:00:00+02:00">
<meta itemprop="keywords" content="vps"><div class="js-article-content">
<div class="layout--article"><!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page">&#8679;</button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody"><details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><h2 id="matrix-synapse-home-server">Matrix Synapse Home Server</h2>
<p><u>Article original</u> : <a href="https://upcloud.com/resources/tutorials/install-matrix-synapse/">How to install Matrix Synapse Home Server</a></p>
<p><img src="/images/matrix-logo.png" alt="Logo Matrix" /></p>
<p>Ce guide vous aidera à installer et à démarrer avec Matrix et son serveur domestique de référence, Synapse. Les instructions et les exemples de commandes contenus dans ce guide vous aideront à configurer Matrix Synapse sur un serveur cloud fonctionnant sous CentOS 8, Debian 10 ou Ubuntu 18</p>
<h3 id="quest-ce-que-matrix-">Quest-ce que Matrix ?</h3>
<p>Matrix est une norme ouverte pour la communication décentralisée, qui distribue de manière sécurisée les salons de discussion persistants sur une fédération ouverte de serveurs, évitant ainsi tout point de contrôle ou de défaillance unique.</p>
<p>Matrix fournit des API HTTP simples et des implémentations de référence open-source pour créer un tissu commun permettant dinterconnecter les îlots de communication existants. Parmi les cas dutilisation, citons la création et la gestion de conversations entièrement distribuées, lactivation dappels VoIP et vidéo WebRTC à laide de la signalisation Matrix, et la synchronisation de lhistorique sur tous les clients en temps réel.</p>
<h3 id="1-déployer-un-serveur-en-nuage">1. Déployer un serveur en nuage</h3>
<p>Si vous nêtes pas encore inscrit à UpCloud, vous devez <a href="https://signup.upcloud.com/%20/?promo=matrix">commencer ici</a> pour bénéficier de 25 $ de crédits en vous inscrivant aujourdhui.<br />
Une fois que vous vous êtes inscrit, connectez-vous à votre <a href="https://hub.upcloud.com/deploy">panneau de contrôle UpCloud</a> et déployez un serveur.</p>
<p><strong>Synapse</strong> nécessite un système compatible POSIX, tel que les distributions Linux susmentionnées, Python 3.6 ou plus récent installé, et au moins 1 Go de RAM libre pour pouvoir rejoindre de grands espaces publics tels que le <code class="language-plaintext highlighter-rouge">#matrix:matrix.org</code>.</p>
<p>Déployez une nouvelle instance de cloud pour héberger le serveur domestique Matrix en utilisant lun des systèmes dexploitation Linux testés vers une zone de disponibilité de votre choix. Vous trouverez des instructions détaillées sur toutes les options de configuration dans le guide de déploiement dun serveur.</p>
<h3 id="2-installation-des-prérequis">2. Installation des prérequis</h3>
<p>Lorsque votre serveur est opérationnel, utilisez les commandes ci-dessous appropriées à votre système pour installer les paquets et les bibliothèques nécessaires.</p>
<p>Pour le système Debian 10, installez les prérequis à laide de la commande suivante.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install build-essential python3-dev libffi-dev \
python-pip python-setuptools sqlite3 \
libssl-dev python-virtualenv libjpeg-dev libxslt1-dev
</code></pre></div></div>
<p>Pour le système Debian 11 , seul python3 est installé</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install build-essential python3-dev libffi-dev \
python3-pip python3-setuptools sqlite3 \
libssl-dev python3-virtualenv libjpeg-dev libxslt1-dev
</code></pre></div></div>
<h3 id="3-installation-du-serveur-domestique-synapse">3. Installation du serveur domestique Synapse</h3>
<p><strong>Synapse</strong> est limplémentation de référence Matrix dun “serveur domestique” écrit par léquipe de développement de base de Matrix. Il est conçu comme une vitrine du concept Matrix pour démontrer la spécification dans le contexte dune base de code. Il vous permet dexécuter facilement votre propre serveur domestique et contribue généralement à lamorçage de lécosystème Matrix.</p>
<p>Les serveurs domestiques sont utilisés comme point daccès pour les clients qui se connectent au réseau Matrix. Ils stockent lhistorique de chat personnel et les informations de compte des utilisateurs de la même manière que le ferait un serveur IMAP/SMTP. Comme pour la messagerie électronique, vous pouvez utiliser votre propre serveur domestique Matrix pour garder le contrôle et la propriété de vos communications et de votre historique ou utiliser lun des services hébergés de Matrix. Matrix na pas de point de contrôle unique ni de fournisseur de services obligatoire.</p>
<p><img src="/images/matrix-synapse001.png" alt="" /></p>
<p>Pour préparer le système, configurez un nouvel environnement virtuel sous ~/synapse à laide des commandes ci-dessous ou choisissez un autre répertoire en remplaçant le ~/synapse dans les commandes par le dossier dans lequel vous souhaitez stocker les fichiers du serveur.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p ~/synapse
virtualenv -p python3 ~/synapse/env
source ~/synapse/env/bin/activate
</code></pre></div></div>
<p>Le prompt suivant apparaît : <code class="language-plaintext highlighter-rouge">(env) hmsuser@server32771:~$</code></p>
<p>Vérifiez que les paquets suivants sont installés et à jour.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install --upgrade pip virtualenv six packaging appdirs
</code></pre></div></div>
<p>Utilisez ensuite pip pour installer et mettre à jour les outils dinstallation ainsi que le serveur Synapse lui-même.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install --upgrade setuptools
pip install matrix-synapse
</code></pre></div></div>
<p>Ceci installe le serveur domestique Synapse ainsi que ses bibliothèques.</p>
<p>Si vous rencontrez des problèmes lors de linstallation du serveur domestique Synapse, Matrix vous donne quelques conseils de dépannage sur la <a href="https://github.com/matrix-org/synapse/blob/master/README.rst#id3">page GitHub de Synapse (en)</a>.</p>
<h3 id="4-configuration-de-synapse">4. Configuration de Synapse</h3>
<p>Avant de pouvoir démarrer Synapse, vous devez générer un fichier de configuration qui définit les paramètres du serveur.</p>
<p>Exécutez les commandes suivantes dans votre environnement virtuel Synapse.<br />
Remplacez le nom du serveur <strong>matrix.yanfi.space</strong> par votre propre domaine, mais notez que le nom ne pourra pas être modifié ultérieurement sans réinstaller le serveur domestique.<br />
Choisissez également si vous souhaitez autoriser Synapse à établir des rapports statistiques en saisissant <code class="language-plaintext highlighter-rouge">--report-stats=yes ou no</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source ~/synapse/env/bin/activate
pip install -U matrix-synapse
cd ~/synapse
python -m synapse.app.homeserver --generate-config -H matrix.yanfi.space --report-stats yes -c homeserver.yaml
</code></pre></div></div>
<p>Réponse</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Generating config file homeserver.yaml
Generating log config file /home/hmsuser/synapse/matrix.yanfi.space.log.config which will log to /home/hmsuser/synapse/homeserver.log
Generating signing key file /home/hmsuser/synapse/matrix.yanfi.space.signing.key
A config file has been generated in 'homeserver.yaml' for server name 'matrix.yanfi.space'. Please review this file and customise it to your needs.
</code></pre></div></div>
<p>Une fois que Synapse a fini de générer les paramètres requis, vous devriez voir une confirmation comme dans lexemple ci-dessus.</p>
<h3 id="5-activation-de-tls">5. Activation de TLS</h3>
<p>Matrix Synapse requiert maintenant lactivation de TLS par défaut pour permettre lutilisation sécurisée du serveur. La façon la plus simple de configurer TLS est dobtenir des certificats SSL auprès dune autorité de certification de confiance telle que <strong>Lets Encrypt</strong>.</p>
<p>On peut générer les certificats par <strong>Certbot</strong> ou <strong>acme.sh</strong></p>
<h4 id="certbot">Certbot</h4>
<p>Le client Certbot de Lets Encrypt est principalement distribué via les paquets Snap. Consultez ci-dessous les instructions pour installer Snap et le client Certbot pour votre système dexploitation.</p>
<p>Les modèles dOS pour Ubuntu 18 et 20 sur UpCloud ont Snap installé par défaut. Vérifiez simplement que Snap est à sa dernière version et utilisez ensuite Snap pour installer Certbot.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>snapd <span class="nt">-y</span>
<span class="nb">sudo </span>snap <span class="nb">install </span>core<span class="p">;</span> <span class="nb">sudo </span>snap refresh core
<span class="nb">sudo </span>snap <span class="nb">install</span> <span class="nt">--classic</span> certbot
<span class="nb">sudo ln</span> <span class="nt">-s</span> /snap/bin/certbot /usr/bin/certbot
</code></pre></div></div>
<p>Vous devrez également disposer dun serveur web que Certbot pourra utiliser pour valider le certificat. Installez <strong>nginx</strong> en suivant les instructions ci-dessous en fonction de votre système dexploitation.</p>
<p>Sur les systèmes Debian et Ubuntu, exécutez simplement la commande suivante.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt install nginx
</code></pre></div></div>
<p>Lorsque le client <strong>Certbot</strong> et <strong>Nginx</strong> sont installés, vous pouvez obtenir des certificats en utilisant la commande <code class="language-plaintext highlighter-rouge">certonly</code> avec le plugin <code class="language-plaintext highlighter-rouge">--nginx</code>.<br />
Incluez au moins un nom de domaine en remplaçant la matrice.exemple.com par la vôtre.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo certbot certonly --nginx -d matrix.yanfi.space
</code></pre></div></div>
<p>Cette commande lance un script de configuration interactif qui pose quelques questions pour faciliter la gestion des certificats.</p>
<ol>
<li>Lors de la première installation sur un hôte spécifique, vous devrez entrer une adresse électronique de contact</li>
<li>puis lire les conditions dutilisation de Lets Encrypt et sélectionner Accepter si vous acceptez les conditions et souhaitez utiliser le service</li>
<li>choisir si vous souhaitez partager votre adresse électronique avec lElectronic Frontier Foundation (EFF) pour recevoir des mises à jour sur leur travail.</li>
</ol>
<p>Si le client a réussi à obtenir un certificat, vous trouverez une confirmation et la date dexpiration du certificat à la fin de la sortie du client.</p>
<p>Si vous rencontrez des problèmes avec le client, assurez-vous que vous essayez denregistrer un domaine ou un sous-domaine qui se résout actuellement sur votre serveur, car les configurations DNS peuvent prendre un certain temps à se propager. Vérifiez également que vous exécutez la commande avec des privilèges dadministrateur.</p>
<p>Lorsque les certificats ont été émis avec succès, vous devez les activer pour le serveur domestique Synapse.</p>
<p>Vous pouvez en savoir plus sur le client Lets Encrypt, y compris sur la manière dautomatiser le renouvellement des certificats, dans notre <a href="https://upcloud.com/community/tutorials/%20/install-lets-encrypt-nginx/">article sur linstallation de Lets Encrypt sur nginx</a>.</p>
<h4 id="acmesh">acme.sh</h4>
<p>1-Installation gestionnaire des certificats Lets Encrypt</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh
./acme.sh --install
</code></pre></div></div>
<p>2-Se reconnecter</p>
<p>3-Exporter les clés OVH API</p>
<p>4-Génération des certificats</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>acme.sh --dns dns_ovh --server letsencrypt --issue --keylength ec-384 -d 'yanfi.space' -d 'matrix.yanfi.space'
</code></pre></div></div>
<p>Ouvrir le lien dauthentification et relancer la commande précédente après le message “OVH authentication Success !” et patienter…</p>
<p>5-Installation des certificats</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mkdir -p /etc/ssl/private/
sudo chown $USER -R /etc/ssl/private/
acme.sh --ecc --install-cert -d 'yanfi.space' -d '*.yanfi.space' --key-file /etc/ssl/private/yanfi.space-key.pem --fullchain-file /etc/ssl/private/yanfi.space-fullchain.pem --reloadcmd 'sudo systemctl reload nginx.service'
</code></pre></div></div>
<p>Editer le crontab</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>crontab -e
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>13 0 * * * "/home/userhms/.acme.sh"/acme.sh --cron --home "/home/userhms/.acme.sh" --renew-hook "/home/userhms/.acme.sh/acme.sh --ecc --install-cert -d 'yanfi.space' -d '*.yanfi.space' --key-file /etc/ssl/private/yanfi.space-key.pem --fullchain-file /etc/ssl/private/yanfi.space-fullchain.pem --reloadcmd 'sudo systemctl reload nginx.service'" &gt; /dev/null
</code></pre></div></div>
<h3 id="6-configuration-du-reverse-proxy-avec-nginx">6. Configuration du reverse proxy avec nginx</h3>
<p>Matrix recommande de configurer un proxy inverse, tel que nginx, Apache ou HAProxy, en amont de votre serveur Synapse. Lobjectif est de simplifier les connexions client en permettant à Matrix dutiliser le port HTTPS commun 443 tout en conservant les connexions serveur à serveur sur le port 8448.</p>
<p>Modifiez la configuration par défaut du serveur domestique pour quil écoute uniquement ladresse localhost pour le port 8008. Ouvrez le fichier homeserver.yaml pour le modifier.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano ~/synapse/homeserver.yaml
</code></pre></div></div>
<p>Trouvez le segment suivant et définissez le bind only sur ladresse 127.0.0.1 et vérifiez que le drapeau forward est défini sur true comme indiqué ci-dessous. Vous pouvez laisser le reste tel quel, nous allons terminer le SSL au niveau du proxy afin que Matrix nait pas à se soucier des paramètres TLS.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> - port: 8008
tls: false
bind_addresses: ['127.0.0.1']
type: http
x_forwarded: true
</code></pre></div></div>
<p>Sauvegardez le fichier et quittez.</p>
<p>Ensuite, permettez à nginx dagir en tant que proxy inverse en créant un fichier de configuration pour la fonctionnalité de proxy.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/nginx/conf.d/matrix.conf
</code></pre></div></div>
<p>Ensuite, entrez ce qui suit pour activer le proxy avec la terminaison SSL. Remplacez matrix.yanfi.space par votre domaine dans le nom du serveur.</p>
<p>Les certificats émis par Lets Encrypt sont enregistrés dans le répertoire indiqué dans la sortie de <strong>Certbot</strong>, généralement sous <code class="language-plaintext highlighter-rouge">/etc/letsencrypt/live/</code>:<br />
/etc/letsencrypt/live/matrix.yanfi.space/fullchain.pem<br />
/etc/letsencrypt/live/matrix.yanfi.space/privkey.pem</p>
<p>ou pour <strong>acme.sh</strong>, <code class="language-plaintext highlighter-rouge">/etc/ssl/private/</code>: <br />
/etc/ssl/private/yanfi.space-fullchain.pem<br />
/etc/ssl/private/yanfi.space-key.pem</p>
<p>Encore une fois, remplacez matrix.yanfi.space par le nom de domaine pour lequel les certificats ont été émis.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
listen 80;
listen [::]:80;
server_name matrix.yanfi.space;
return 301 https://$host$request_uri;
}
server {
listen 443 http2;
listen [::]:443 http2;
server_name matrix.yanfi.space;
ssl_certificate /etc/ssl/private/yanfi.space-fullchain.pem;
ssl_certificate_key /etc/ssl/private/yanfi.space-key.pem;
location / {
proxy_pass http://localhost:8008;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
server {
listen 8448 ssl default_server;
listen [::]:8448 ssl default_server;
server_name matrix.yanfi.space;
ssl_certificate /etc/ssl/private/yanfi.space-fullchain.pem;
ssl_certificate_key /etc/ssl/private/yanfi.space-key.pem;
location / {
proxy_pass http://localhost:8008;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
</code></pre></div></div>
<p>La configuration nginx ci-dessus comprend trois parties pour faciliter au maximum lutilisation de votre serveur domestique Matrix Synapse. Il écoute les connexions sécurisées sur les ports 443 pour HTTPS et 8448 pour la communication entre serveurs de la fédération Matrix. Les connexions au port HTTP 80 non sécurisé sont redirigées pour utiliser HTTPS à la place.</p>
<p>Une fois que vous avez terminé, enregistrez le fichier et quittez léditeur.</p>
<p>Ensuite, démarrez et activez nginx.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart nginx
sudo systemctl enable nginx
</code></pre></div></div>
<p>Cest tout ce dont vous avez besoin pour configurer un serveur domestique Synapse. Continuez ci-dessous pour démarrer le serveur et créer votre premier utilisateur.</p>
<h3 id="7-enregistrement-dun-nouvel-utilisateur">7. Enregistrement dun nouvel utilisateur</h3>
<p>Maintenant que Synapse est installé, vous devez ajouter un nouvel utilisateur en tant quadministrateur pour pouvoir configurer les salles de chat et les paramètres du client Web. Tout dabord, assurez-vous que vous êtes dans lenvironnement virtuel du serveur domestique et démarrez le serveur.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~/synapse
source env/bin/activate
synctl start
</code></pre></div></div>
<p>Résultat de la commande précédente</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>This server is configured to use 'matrix.org' as its trusted key server via the
'trusted_key_servers' config option. 'matrix.org' is a good choice for a key
server since it is long-lived, stable and trusted. However, some admins may
wish to use another server for this purpose.
To suppress this warning and continue using 'matrix.org', admins should set
'suppress_key_server_warning' to 'true' in homeserver.yaml.
--------------------------------------------------------------------------------
started synapse.app.homeserver(homeserver.yaml)
</code></pre></div></div>
<p>Ensuite, enregistrez un nouvel utilisateur sur le localhost.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>register_new_matrix_user -c homeserver.yaml http://localhost:8008
</code></pre></div></div>
<p>Ceci démarre une configuration interactive de lutilisateur, entrez le nom dutilisateur et le mot de passe désirés, puis sélectionnez yes pour activer les privilèges dadministration.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>New user localpart [root]: username
Password: password
Confirm password: password
Make admin [no]: yes
Sending registration request...
Success.
</code></pre></div></div>
<p>Voilà, vous devriez maintenant être en mesure de vous connecter à votre serveur domestique Synapse avec les informations didentification de lutilisateur que vous venez de créer.</p>
<h3 id="8-connexion-à-matrix">8. Connexion à Matrix</h3>
<p>Les utilisateurs ont la possibilité de se connecter à un serveur domestique Matric en utilisant un ou plusieurs clients Matrix. Tout dabord, vérifiez que le serveur Matrix fonctionne et quil est joignable en ouvrant votre domaine sur un navigateur Web.</p>
<p>https://matrix.yanfi.space</p>
<p>Vous devriez voir une page telle que celle ci-dessous.<br />
<img src="/images/matrix-synapse002.png" alt="" width="400" /></p>
<p>Le serveur domestique ne comprend pas de client Matrix par défaut, mais vous pouvez utiliser le client Web Riot. Il possède une interface utilisateur soignée et facile à utiliser, et après avoir installé des certificats de confiance à laide de Lets Encrypt, vous pouvez vous connecter à votre serveur domestique à laide dun client public tel que Riot.</p>
<p>Allez à lURL suivante sur votre navigateur web <a href="https://riot.im/app/#/login">https://riot.im/app/#/login</a></p>
<p>Vous devriez voir une page de connexion comme dans limage dexemple ci-dessous. Tout dabord, cliquez sur le lien “Modifier” pour choisir dutiliser votre serveur Matrix personnalisé.<br />
<img src="/images/matrix-synapse003.png" alt="" width="400" /><br />
Entrez le nom de domaine de votre serveur Matrix comme URL de votre serveur daccueil et cliquez sur le bouton <strong>Continuer</strong>.<br />
<img src="/images/matrix-synapse004.png" alt="" width="400" /><br />
Riot effectuera alors une vérification rapide de la connexion à votre serveur cloud. Si vous voyez un message derreur, vérifiez le domaine de votre serveur et que vous lavez entré en utilisant HTTPS.</p>
<p>Ensuite, connectez-vous avec le nom dutilisateur et le mot de passe que vous venez de créer dans la section précédente pour votre propre serveur domestique.<br />
<img src="/images/matrix-synapse005.png" alt="" width="400" /></p>
<p>Après quelques instants<br />
<img src="/images/matrix-synapse006.png" alt="" width="600" /><br />
Lapplication Web Riot est un moyen pratique de tester votre serveur Matrix Synapse personnalisé, mais ce nest certainement pas le seul client disponible. Dautres applications clientes sont disponibles sur la <a href="https://matrix.org/docs/projects/try-matrix-now/#clients">page de documentation de Matrix</a>.</p>
<h3 id="9-création-dune-pièce">9. Création dune pièce</h3>
<p>Ensuite, vous souhaiterez peut-être créer une nouvelle salle. Dans Matrix, tout se passe dans des salles qui sont distribuées et nexistent pas sur un seul serveur. Les salles peuvent être identifiées et localisées en utilisant des alias tels que #matrix:matrix.org ou #test:localhost:8008.</p>
<p>Cliquez sur licône “+” de “Salons” et choisir “Nouveau salon”<br />
<img src="/images/matrix-synapse007.png" alt="" width="400" /></p>
<p>Donnez un nom à votre espace et, avec le client Synapse, vous pouvez également rendre lespace public si vous le souhaitez.<br />
<img src="/images/matrix-synapse008.png" alt="" width="400" /><br />
Appuyez ensuite sur la touche Entrée pour enregistrer le nom et rejoindre la nouvelle pièce.
<img src="/images/matrix-synapse009.png" alt="" width="600" /><br />
Les salles peuvent être utilisées comme nimporte quel canal de communication, mais Matrix est conçu pour être beaucoup plus performant quun simple chat textuel. La mise en œuvre de base du serveur domestique et du client Web Synapse nest quun exemple de la manière dont le réseau Matrix peut être utilisé. Il existe déjà un certain nombre dautres options pour les serveurs domestiques avec de nombreux cas dutilisation différents. Vous trouverez dautres <a href="https://matrix.org/docs/projects/try-matrix-now.html#servers">serveurs dapplication accessibles au public</a> dans la documentation des projets Matrix.</p>
<h3 id="10-synapse-systemd">10. Synapse systemd</h3>
<p>Fichier de configuration systemd pour synapse.<br />
Synapse a été installé par un utilisateur nommé <strong>hmsuser</strong> dans une virtualenv dans le répertoire personnel de lutilisateur : <code class="language-plaintext highlighter-rouge">/home/hmsuser/synapse/env</code></p>
<p>Fichier <code class="language-plaintext highlighter-rouge">/etc/systemd/system/matrix-synapse.service</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Synapse Matrix homeserver
# If you are using postgresql to persist data, uncomment this line to make sure
# synapse starts after the postgresql service.
# After=postgresql.service
[Service]
Type=notify
NotifyAccess=main
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-abort
User=hmsuser
Group=nogroup
WorkingDirectory=/home/hmsuser/synapse
ExecStart=/home/hmsuser/synapse/env/bin/python -m synapse.app.homeserver --config-path=/home/hmsuser/synapse/homeserver.yaml
SyslogIdentifier=matrix-synapse
# adjust the cache factor if necessary
# Environment=SYNAPSE_CACHE_FACTOR=2.0
[Install]
WantedBy=multi-user.target
</code></pre></div></div>
<p>Lancer et activer</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable matrix-synapse
sudo systemctl start matrix-synapse
</code></pre></div></div>
<h3 id="cest-fait-">Cest fait !</h3>
<p>Félicitations, vous avez maintenant déployé et configuré avec succès votre premier serveur domestique Matrix à laide de Synapse.</p>
<p>Si vous souhaitez essayer dautres clients, le plugin Weechat Matrix permet de se connecter même en ligne de commande, tandis que Riot est également disponible sur Android et iOS. Enfin, ceux qui ont le savoir-faire technique peuvent toujours écrire leur propre client à laide de lun des SDK de Matrix.</p>
<p>Matrix présente un potentiel énorme en matière de communication sécurisée, quil sagisse de salons de discussion de groupe, dappels VoIP et vidéo ou déchange de données persistantes entre appareils et services dans lInternet des objets. Nhésitez pas à expérimenter avec les serveurs et les clients existants ou à développer les vôtres !</p>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2022-07-21T00:00:00+02:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2022/07/21/HostMyServers-VPS-server32771-Debian-Bullseye.html">HostMyServers, server32771 Debian 11 (Bullseye)</a></div><div class="next"><span>SUIVANT</span><a href="/2022/08/01/Archlinux_conteneur_LXC_debian_11_Bullseye.html">Archlinux --> conteneur LXC debian bullseye lxcyan</a></div></div></div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div></div>
</div><script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div></div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>