autogenerating sidebar links
This commit is contained in:
parent
1546fa8199
commit
6f5cbd21aa
13 changed files with 522 additions and 34 deletions
51
README.md
51
README.md
|
|
@ -53,3 +53,54 @@ All commands are run from the root of the project, from a terminal:
|
|||
## 👀 Want to learn more?
|
||||
|
||||
Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
|
||||
|
||||
## Todo
|
||||
|
||||
Offer a way to collate all pages into one for PDF/printing.
|
||||
|
||||
- https://github.com/withastro/starlight/discussions/964
|
||||
|
||||
Additional links in navbar:
|
||||
|
||||
- https://starlight-utils.pages.dev/utilities/nav-links/
|
||||
|
||||
Colors:
|
||||
|
||||
- https://github.com/withastro/starlight/blob/a1926ce87b43aaa25b9063f9776e8e4ffabed88d/packages/starlight/style/props.css#L2
|
||||
|
||||
Quiz:
|
||||
|
||||
|
||||
<Box icon="question-mark">
|
||||
|
||||
## Test your knowledge
|
||||
|
||||
Which of the following is a code editor, for making changes to your files and their content?
|
||||
|
||||
<MultipleChoice>
|
||||
<Option>
|
||||
web browser
|
||||
</Option>
|
||||
<Option>
|
||||
Terminal
|
||||
</Option>
|
||||
<Option isCorrect>
|
||||
VS Code
|
||||
</Option>
|
||||
</MultipleChoice>
|
||||
|
||||
Which of the following is a code editor, for making changes to your files and their content?
|
||||
|
||||
<MultipleChoice>
|
||||
<Option>
|
||||
web browser
|
||||
</Option>
|
||||
<Option>
|
||||
Terminal
|
||||
</Option>
|
||||
<Option isCorrect>
|
||||
VS Code
|
||||
</Option>
|
||||
</MultipleChoice>
|
||||
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -17,29 +17,8 @@ export default defineConfig({
|
|||
sidebar: [
|
||||
{
|
||||
label: "ISPmail for Debian 12",
|
||||
items: [
|
||||
// Each item here is one entry in the navigation menu.
|
||||
{
|
||||
label: "Start Here",
|
||||
slug: "ispmail-bookworm",
|
||||
},
|
||||
{
|
||||
label: "Whats' new",
|
||||
slug: "ispmail-bookworm/whats-new",
|
||||
},
|
||||
{
|
||||
label: "Migrating your old server",
|
||||
slug: "ispmail-bookworm/migrating-from-a-bullseye-to-a-bookworm-server",
|
||||
},
|
||||
{
|
||||
label: "The big picture",
|
||||
slug: "ispmail-bookworm/big-picture",
|
||||
},
|
||||
{
|
||||
label: "Types of email domains",
|
||||
slug: "ispmail-bookworm/types-of-email-domains",
|
||||
},
|
||||
],
|
||||
// slug: "ispmail-bookworm",
|
||||
autogenerate: { directory: "ispmail-debian-12" },
|
||||
},
|
||||
// {
|
||||
// label: "Reference",
|
||||
|
|
|
|||
79
src/components/Box.astro
Normal file
79
src/components/Box.astro
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
import { getImage } from "astro:assets";
|
||||
import HoustonGrad from "~/assets/houston_grad.png";
|
||||
|
||||
export interface Props {
|
||||
icon?: "puzzle-piece" | "question-mark" | "check-list";
|
||||
}
|
||||
|
||||
const { icon } = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class={`highlight-box content ${icon}`}
|
||||
style={icon === "question-mark" ? `--box-icon-url: url('nah');` : ""}
|
||||
>
|
||||
<section>
|
||||
<slot />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(section > *) + .highlight-box {
|
||||
margin: 4rem 0;
|
||||
}
|
||||
|
||||
.highlight-box {
|
||||
position: relative;
|
||||
padding-block: 2rem;
|
||||
padding-inline-start: 2rem;
|
||||
padding-inline-end: 4rem;
|
||||
border: 1px solid var(--sl-color-blue);
|
||||
background-color: var(--sl-color-blue-low);
|
||||
margin-top: 2.5rem !important;
|
||||
}
|
||||
|
||||
.highlight-box.check-list {
|
||||
border: 1px solid var(--sl-color-green);
|
||||
background-color: var(--sl-color-green-low);
|
||||
}
|
||||
@supports (mask: var(--box-icon-url) no-repeat center bottom / 100%) or
|
||||
(-webkit-mask: var(--box-icon-url) no-repeat center bottom / 100%) {
|
||||
.highlight-box::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -2.5rem;
|
||||
inset-inline-end: 1.5rem;
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
background: var(--box-icon-url) no-repeat center center / 100%;
|
||||
}
|
||||
}
|
||||
|
||||
:global(ol) {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.highlight-box :global(details) {
|
||||
--sl-details-border-color: var(--sl-color-purple);
|
||||
--sl-details-border-color--hover: var(--sl-color-purple-high);
|
||||
}
|
||||
|
||||
@media (max-width: 50em) {
|
||||
.highlight-box::before {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style is:global>
|
||||
.highlight-box.question-mark .heading-wrapper:first-child {
|
||||
margin-inline-end: 6rem !important;
|
||||
}
|
||||
@media (max-width: 50em) {
|
||||
.highlight-box.question-mark .heading-wrapper:first-child {
|
||||
margin-inline-end: 3rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
108
src/components/Checklist.astro
Normal file
108
src/components/Checklist.astro
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<div class="checklist"><slot /></div>
|
||||
{
|
||||
Astro.slots.has('alternative') && (
|
||||
<>
|
||||
<p class="or">
|
||||
foo
|
||||
</p>
|
||||
<div class="checklist alternative">
|
||||
<slot name="alternative" />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
<style>
|
||||
check-list > * + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.checklist > :global(ul) {
|
||||
list-style: none;
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
check-list :global(label) {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin: 0 -1rem !important;
|
||||
border-radius: 0.5rem;
|
||||
padding-block: 0.5rem;
|
||||
padding-inline: 3.5rem 0.5rem;
|
||||
cursor: pointer;
|
||||
font-size: var(--sl-text-lg);
|
||||
color: var(--sl-color-green-high);
|
||||
}
|
||||
|
||||
check-list :global(label span) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
check-list :global(input[type='checkbox']) {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
check-list :global(input[type='checkbox']:focus) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
check-list :global(input[type='checkbox']::after) {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-inline-start: 1rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transform-origin: center;
|
||||
text-align: center;
|
||||
line-height: 1.25;
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
font-size: 1.25em;
|
||||
border: 1px solid var(--sl-color-green-high);
|
||||
border-radius: 0.25rem;
|
||||
color: var(--sl-color-green);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
check-list :global(input[type='checkbox']:focus::after) {
|
||||
outline: 3px solid var(--sl-color-green);
|
||||
outline-offset: 0.5rem;
|
||||
}
|
||||
|
||||
check-list :global(input[type='checkbox']:not(:focus-visible)::after) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
check-list :global(label:has(> input[type='checkbox']:checked)) {
|
||||
color: var(--sl-color-white);
|
||||
}
|
||||
|
||||
check-list :global(input[type='checkbox']:checked::after) {
|
||||
content: url("data:image/svg+xml,%3Csvg width='28' height='28' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='24' height='24' rx='4' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M20.0946 6.02558C20.6992 6.592 20.7301 7.54124 20.1636 8.14578L10.3868 18.5806C9.55548 19.468 8.14726 19.468 7.3159 18.5806L4.02558 15.0689C3.45917 14.4643 3.49008 13.5151 4.09461 12.9487C4.69915 12.3822 5.6484 12.4132 6.21481 13.0177L8.85137 15.8317L17.9744 6.09461C18.5408 5.49007 19.4901 5.45917 20.0946 6.02558Z' fill='%232F4D22'/%3E%3C/svg%3E%0A");
|
||||
border: none;
|
||||
/* color: var(--sl-color-green-low); */
|
||||
/* background-color: var(--sl-color-gray-1); */
|
||||
}
|
||||
|
||||
.or {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
gap: 1em;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
color: var(--theme-text-lighter);
|
||||
}
|
||||
|
||||
.or::before,
|
||||
.or::after {
|
||||
--divider-width: 4px;
|
||||
content: '';
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
transparent calc(50% - var(--divider-width) / 2),
|
||||
var(--theme-divider) calc(50% - var(--divider-width) / 2) calc(50% + var(--divider-width) / 2),
|
||||
transparent calc(50% + var(--divider-width) / 2) 100%
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,7 +6,7 @@ import Default from "@astrojs/starlight/components/Footer.astro";
|
|||
<footer>
|
||||
<Default {...Astro.props}><slot /></Default>
|
||||
<div class="myfooter">
|
||||
Copyright © 2003-2024 Christoph Haas. All content on workaround.org can be used under the terms of the
|
||||
This guide is maintained by Christoph Haas since 2003. All content on workaround.org can be used under the terms of the
|
||||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">Creative Commons BY-NC-SA license</a>. Penguin images
|
||||
based on AI art because I suck at drawing.
|
||||
</div>
|
||||
|
|
|
|||
167
src/components/MultipleChoice.astro
Normal file
167
src/components/MultipleChoice.astro
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<multiple-choice data-correct-label={"Correct"} data-incorrect-label={"Wrong"}>
|
||||
<form onsubmit="event.preventDefault()">
|
||||
<ol class="opt-list not-content"><slot /></ol>
|
||||
<div class="footer">
|
||||
<button class="submit" type="submit" disabled>
|
||||
{"Submit"}
|
||||
</button>
|
||||
<div class="answer sr-only" role="alert"></div>
|
||||
</div>
|
||||
</form>
|
||||
</multiple-choice>
|
||||
|
||||
<style>
|
||||
multiple-choice {
|
||||
display: block;
|
||||
padding-bottom: 1rem;
|
||||
color: var(--sl-color-text-accent);
|
||||
}
|
||||
|
||||
form > * + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.opt-list {
|
||||
list-style: none;
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
/* .footer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
} */
|
||||
|
||||
.submit,
|
||||
.answer {
|
||||
padding: 0.8rem;
|
||||
line-height: 1.4;
|
||||
max-width: 15em;
|
||||
border: 2px solid var(--sl-color-gray-4);
|
||||
}
|
||||
|
||||
.submit {
|
||||
cursor: pointer;
|
||||
color: var(--sl-color-accent);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.submit:focus {
|
||||
background-color: hsl(224, 20%, 94%);
|
||||
}
|
||||
|
||||
.submit:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.submit:hover {
|
||||
background: var(--sl-color-text-accent);
|
||||
color: var(--sl-color-black);
|
||||
/* border-color: transparent; */
|
||||
}
|
||||
|
||||
.submit:disabled,
|
||||
.submit:active {
|
||||
/* transform: translateY(0.25rem); */
|
||||
}
|
||||
|
||||
.submit:disabled {
|
||||
background-color: hsl(224, 20%, 94%);
|
||||
color: hsl(224, 7%, 36%);
|
||||
opacity: 0.65;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.answer {
|
||||
text-align: center;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.correct {
|
||||
color: var(--sl-color-green-low);
|
||||
background-color: var(--sl-color-green);
|
||||
}
|
||||
|
||||
.wrong {
|
||||
color: var(--sl-color-red-low);
|
||||
background-color: var(--sl-color-red);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
class MultipleChoice extends HTMLElement {
|
||||
#defaultCorrectLabel: string;
|
||||
#defaultIncorrectLabel: string;
|
||||
#key: string;
|
||||
#submitEl: HTMLButtonElement;
|
||||
#answerEl: HTMLParagraphElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#defaultCorrectLabel = this.dataset.correctLabel!;
|
||||
this.#defaultIncorrectLabel = this.dataset.incorrectLabel!;
|
||||
this.#key = Math.random().toString();
|
||||
this.#submitEl = this.querySelector(".submit")!;
|
||||
this.#answerEl = this.querySelector(".answer")!;
|
||||
this.querySelectorAll(".opt-list > li").forEach((li) => this.#upgradeListItem(li));
|
||||
}
|
||||
|
||||
#upgradeListItem(li: Element) {
|
||||
const option = li.querySelector('input[type="radio"]') as HTMLInputElement | null;
|
||||
if (!option) return;
|
||||
|
||||
option.removeAttribute("disabled");
|
||||
option.setAttribute("name", this.#key);
|
||||
option.addEventListener("change", () => {
|
||||
this.#clearAnswer();
|
||||
this.#enableSubmit();
|
||||
});
|
||||
if (option.checked) this.#enableSubmit();
|
||||
}
|
||||
|
||||
/** Clear the answer text and hide its container visually. */
|
||||
#clearAnswer() {
|
||||
this.#answerEl.innerText = "";
|
||||
this.#answerEl.classList.remove("wrong", "correct");
|
||||
this.#answerEl.classList.add("sr-only");
|
||||
}
|
||||
|
||||
/** Show the answer result to the user. */
|
||||
#setAnswer(isCorrect: boolean) {
|
||||
const answerTemplate = this.querySelector("input:checked ~ template") as HTMLTemplateElement | null;
|
||||
if (answerTemplate) {
|
||||
this.#answerEl.replaceChildren(answerTemplate.content.cloneNode(true));
|
||||
} else {
|
||||
this.#answerEl.innerText = isCorrect ? this.#defaultCorrectLabel : this.#defaultIncorrectLabel;
|
||||
}
|
||||
this.#answerEl.classList.remove("sr-only", "wrong", "correct");
|
||||
this.#answerEl.classList.add(isCorrect ? "correct" : "wrong");
|
||||
}
|
||||
|
||||
/** Activate the submit button, preparing it to evaluate the form when clicked. */
|
||||
#enableSubmit() {
|
||||
this.#submitEl.removeAttribute("disabled");
|
||||
this.#submitEl.classList.remove("sr-only");
|
||||
this.#submitEl.onclick = () => this.#submitAnswer();
|
||||
}
|
||||
|
||||
/** Disable the submit button and hide it visually. */
|
||||
#disableSubmit() {
|
||||
this.#submitEl.setAttribute("disabled", "");
|
||||
this.#submitEl.classList.add("sr-only");
|
||||
this.#submitEl.onclick = null;
|
||||
}
|
||||
|
||||
/** Check if the selected option is correct and display the result. */
|
||||
#submitAnswer() {
|
||||
const selection = this.querySelector("input:checked") as HTMLInputElement | null;
|
||||
if (!selection) return;
|
||||
|
||||
this.#disableSubmit();
|
||||
const isCorrect = selection.dataset.isCorrect !== undefined && ["", "true"].includes(selection.dataset.isCorrect);
|
||||
this.#setAnswer(isCorrect);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("multiple-choice", MultipleChoice);
|
||||
</script>
|
||||
87
src/components/Option.astro
Normal file
87
src/components/Option.astro
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
export interface Props {
|
||||
isCorrect?: boolean;
|
||||
}
|
||||
|
||||
const { isCorrect } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<li>
|
||||
<label>
|
||||
<input type="radio" disabled data-is-correct={isCorrect} /><span><slot /></span>
|
||||
{
|
||||
Astro.slots.has('feedback') && (
|
||||
<template>
|
||||
<slot name="feedback" />
|
||||
</template>
|
||||
)
|
||||
}
|
||||
</label>
|
||||
</li>
|
||||
|
||||
<style>
|
||||
label {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
border-radius: 1rem;
|
||||
margin-inline: -0.5rem;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label:hover {
|
||||
background-color: var(--sl-color-accent-low);
|
||||
}
|
||||
|
||||
input[type='radio'] {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type='radio'] ~ * {
|
||||
color: var(--sl-color-text-accent);
|
||||
}
|
||||
|
||||
input[type='radio']:checked ~ * {
|
||||
color: var(--sl-color-white);
|
||||
}
|
||||
|
||||
input[type='radio']:focus::after {
|
||||
outline: 3px solid var(--sl-color-text-accent);
|
||||
outline-offset: 0.5rem;
|
||||
}
|
||||
|
||||
input[type='radio']:focus,
|
||||
input[type='radio']:not(:focus-visible)::after {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type='radio']::after {
|
||||
display: block;
|
||||
content: '';
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
font-size: 1em;
|
||||
font-weight: 900;
|
||||
border: 2px solid var(--sl-color-accent-high);
|
||||
border-radius: 100%;
|
||||
color: var(--sl-color-white);
|
||||
box-shadow: -3px 3px var(--theme-shade-subtle);
|
||||
}
|
||||
|
||||
input[type='radio']:checked::after {
|
||||
color: hsl(var(--color-base-white), 100%);
|
||||
background-image: radial-gradient(var(--sl-color-white) 50%, transparent 0%, transparent);
|
||||
border-color: var(--sl-color-white);
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
input[type='radio']:checked::after {
|
||||
background-color: Highlight;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
10
src/components/UIString.astro
Normal file
10
src/components/UIString.astro
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
import type { UIDictionaryKeys } from '../i18n/translation-checkers';
|
||||
import { useTranslations } from '../i18n/util';
|
||||
|
||||
export interface Props {
|
||||
key: UIDictionaryKeys;
|
||||
}
|
||||
---
|
||||
|
||||
{useTranslations(Astro)(Astro.props.key)}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
title: ISPmail guide for Debian 12 “Bookworm”
|
||||
lastUpdated: 2023-09-24
|
||||
slug: ispmail-bookworm
|
||||
---
|
||||
|
||||
Once upon a time there were many mail servers on the internet. If your organisation wanted to receive and send emails then you would have your system administrator set up a mail server. He would add a DNS record and create a cryptic Sendmail configuration file.
|
||||
|
|
@ -2,6 +2,12 @@
|
|||
title: Migrating from your old (Bullseye) server
|
||||
---
|
||||
|
||||
import MultipleChoice from '../../../components/MultipleChoice.astro';
|
||||
import Option from '../../../components/Option.astro';
|
||||
import Checklist from '../../../components/Checklist.astro';
|
||||
import Box from '../../../components/Box.astro';
|
||||
|
||||
|
||||
## Upgrade or fresh installation?
|
||||
|
||||
I recommend that you set up a new server and make sure that the new mail server is working correctly before you start migrating existing email users to it. You may argue that Debian can be upgraded easily using “apt-get dist-upgrade” but that is very dangerous on a live mail server. Automatic configuration changes may have evil side effects and you risk losing emails. At least you are causing a downtime for your users.
|
||||
|
|
@ -24,12 +30,12 @@ The DNS “MX” record for your domain contains the hostname of your mail serve
|
|||
|
||||
You need to copy the database that contains the control data about your email domains and accounts. Log into the old (Bullseye) server as root and back up the _mailserver_ database. That is as easy as running…
|
||||
|
||||
```sh
|
||||
```
|
||||
mysqldump mailserver > mailserver.sql
|
||||
```
|
||||
Copy that file to the new server (using _scp_) and import it there:
|
||||
|
||||
```sh
|
||||
```
|
||||
mysql mailserver < mailserver.sql
|
||||
```
|
||||
|
||||
|
|
@ -39,13 +45,13 @@ Obviously any database changes on the old server from now on will have to be don
|
|||
|
||||
If your users are using Roundcube as a webmail interface then you should migrate their data like their contact lists. Dump the SQL from the old server:
|
||||
|
||||
```sh
|
||||
```
|
||||
mysqldump roundcube > roundcube.sql
|
||||
```
|
||||
|
||||
Copy that file to the new server and import it:
|
||||
|
||||
```sh
|
||||
```
|
||||
mysql roundcube < roundcube.sql
|
||||
```
|
||||
|
||||
|
|
@ -63,7 +69,7 @@ If you have been using rspamd with the Redis backend then copy over the Redis da
|
|||
|
||||
Fortunately Dovecot uses the maildir format that stores emails as plain files on disk. Login to the new (Bookworm) server and use _rsync_ to copy over the mails from the old (Bullseye) mail server:
|
||||
|
||||
```sh
|
||||
```
|
||||
rsync -va oldserver:/var/vmail/ /var/vmail/
|
||||
```
|
||||
|
||||
|
|
@ -75,7 +81,7 @@ There is no need to shut down Dovecot on your production Bullseye server. Copyin
|
|||
|
||||
Copy over everything in /etc/letsencrypt and /var/lib/rspamd/dkim from your old to the new server.
|
||||
|
||||
```sh
|
||||
```
|
||||
rsync -va oldserver:/etc/letsencrypt/ /etc/letsencrypt/
|
||||
rsync -va oldserver:/var/lib/rspamd/dkim/ /var/lib/rspamd/dkim/
|
||||
```
|
||||
|
|
@ -88,7 +94,7 @@ You told your users about the downtime, right? The time has come? Okay. Shut dow
|
|||
|
||||
Let’s synchronize again. _rsync_ will only copy those files that have changed which makes it much faster than the first sync. On your new server run:
|
||||
|
||||
```sh
|
||||
```
|
||||
rsync -va --delete oldserver:/var/vmail/ /var/vmail/
|
||||
```
|
||||
|
||||
|
|
@ -96,7 +102,7 @@ rsync -va --delete oldserver:/var/vmail/ /var/vmail/
|
|||
|
||||
The new Dovecot version uses a slightly different indexing mechanism. So force rebuilding the users’ indices:
|
||||
|
||||
```sh
|
||||
```
|
||||
doveadm force-resync -A '*'
|
||||
```
|
||||
|
||||
|
|
@ -108,7 +114,7 @@ For all your domains you will have to change the DNS “MX” or “A” record
|
|||
|
||||
Accidents happen. And you don’t want to lose emails. So run this command to enable your safety net on the new server:
|
||||
|
||||
```sh
|
||||
```
|
||||
postconf soft_bounce=yes
|
||||
```
|
||||
|
||||
|
|
@ -116,7 +122,7 @@ This makes Postfix always keep emails in the queue that it would otherwise rejec
|
|||
|
||||
Once you are certain that emails are properly received and sent you can switch off the _soft_bounce_ mode again:
|
||||
|
||||
```sh
|
||||
```
|
||||
postconf soft_bounce=no
|
||||
```
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue