Compare commits

..

28 commits
main ... main

Author SHA1 Message Date
3095f1726e Merge pull request 'Do not render a different view when user is found but password invalid' (#87) from fabientot/guichet:prevent-bruteforce-on-login-form into main
Reviewed-on: Deuxfleurs/guichet#87
2025-03-26 08:27:32 +00:00
b319421c1f Do not render a different view when user is found 2025-03-25 12:29:01 +01:00
48526f6aca Merge pull request 'Add a button to visit the website' (#85) from fabientot/guichet:add-visit-button-to-website into main
Reviewed-on: Deuxfleurs/guichet#85
2025-03-24 15:41:57 +00:00
492be02f59 Merge pull request 'Add instructions about how to run Guichet locally' (#81) from fabientot/guichet:improve-local-development into main
Reviewed-on: Deuxfleurs/guichet#81
2025-03-24 15:41:28 +00:00
4dfd072385 Merge pull request 'Add missing username in dxfl cli instructions' (#84) from fabientot/guichet:fix-missing-username-in-dxfl-cli-docs into main
Reviewed-on: Deuxfleurs/guichet#84
2025-03-24 14:12:19 +00:00
08b036b2fb Add a button to visit the website 2025-03-23 21:47:08 +01:00
c1fcc1bbba Add line breaks 2025-03-23 20:39:25 +01:00
97a0d1ed24 Add password example 2025-03-23 20:37:49 +01:00
56d78d4a1f Update comments 2025-03-23 20:35:12 +01:00
9fef8d855f Typo + link 2025-03-23 20:32:56 +01:00
a7180549ed Add garage in list 2025-03-23 20:31:25 +01:00
59b8ecf02f Add missing username in dxfl cli instructions 2025-03-23 20:21:40 +01:00
0d3457142e Update readme 2025-03-23 20:11:13 +01:00
f8a3714d8c Merge pull request 'Update new DNS endpoint on website's detail page' (#83) from tixie/guichet:update-dns-endpoint-wording into main
Reviewed-on: Deuxfleurs/guichet#83
2025-03-21 06:36:39 +00:00
791f6aa3b8
use global.site.deuxfleurs.fr in website inspector's footer 2025-03-21 05:04:03 +01:00
8a939391d5 Merge pull request 'Quick fixes' (#82) from qdu-fix into main
Reviewed-on: Deuxfleurs/guichet#82
2025-03-20 23:17:56 +00:00
f53b100752
fix hugo 2025-03-21 00:16:19 +01:00
057f84e663
advertise dxfl 2025-03-21 00:10:21 +01:00
e3512bf3f1
switch to global.site.deuxfleurs.fr 2025-03-20 23:59:05 +01:00
371c46e0ed
simplify invitations 2025-03-20 23:57:49 +01:00
917c10737d
improve readme and nix file 2025-03-20 23:34:29 +01:00
4f3b5d8210 Do not expose ports in consul 2025-03-20 23:29:05 +01:00
48df2123cf Improve developper experience
- Add docker compose
- Fix a few typo in README
- Add steps to run project locally
- Add a sample bottin config
2025-03-20 23:27:59 +01:00
6a399249b5 Merge pull request 'template: remove spurious space before secret access keys' (#76) from rm_secret_key_space into main
Reviewed-on: Deuxfleurs/guichet#76
Reviewed-by: aeddis <aeddis@noreply.localhost>
2025-03-20 22:03:13 +00:00
dd3e1833f7 Merge branch 'main' into rm_secret_key_space 2025-03-20 22:03:00 +00:00
34c2ddc29e Merge pull request 'openapi spec: add some documentation about the Vhost record' (#77) from Armael/guichet:openapi_vhost_docs into main
Reviewed-on: Deuxfleurs/guichet#77
Reviewed-by: aeddis <aeddis@noreply.localhost>
2025-03-18 11:12:21 +00:00
Armaël Guéneau
a0e5c1020b openapi spec: add some documentation about the Vhost record 2025-02-24 11:35:50 +01:00
Armaël Guéneau
687fbe8b59 template: remove spurious space before secret access keys
an extra space would otherwise show up when copy/pasting the access
key from guichet, causing confusion
2025-02-22 12:54:21 +01:00
13 changed files with 155 additions and 116 deletions

View file

@ -1,19 +0,0 @@
BIN=guichet
SRC=main.go ssha.go profile.go admin.go invite.go directory.go picture.go
DOCKER=lxpz/guichet_amd64
all: $(BIN)
$(BIN): $(SRC)
go get -d -v
go build -v -o $(BIN)
$(BIN).static: $(SRC)
go get -d -v
CGO_ENABLED=0 GOOS=linux go build -a -v -o $(BIN).static
docker: $(BIN).static
docker build -t $(DOCKER):$(TAG) .
docker push $(DOCKER):$(TAG)
docker tag $(DOCKER):$(TAG) $(DOCKER):latest
docker push $(DOCKER):latest

View file

@ -8,10 +8,10 @@ Guichet is a simple LDAP web interface for the following tasks:
- administration of the LDAP directory
- inviting new users to create accounts
Guichet works well with the [Bottin](https://bottin.eu) LDAP server.
Guichet works well with the [Bottin](https://git.deuxfleurs.fr/deuxfleurs/bottin) LDAP server.
Currently, Guichet's templates are only in French as it has been created for
the [Deuxfleurs](https://deuxfleurs.fr) collective.
We would gladly merge a pull request with an English transaltion !
We would gladly merge a pull request with an English translation !
A Docker image is provided on the [Docker hub](https://hub.docker.com/r/lxpz/guichet_amd64).
An example for running Guichet on a Nomad cluster can be found in `guichet.hcl.example`.
@ -27,9 +27,23 @@ Guichet is licensed under the terms of the GPLv3.
Guichet requires go 1.13 or later.
To build Guichet, clone this repository outside of your `$GOPATH`.
Then, run `make` in the root of the repo.
Development build:
```
go build
```
Production build:
```
nix build
```
Production container:
```
docker run .#docker
```
## Configuration of Guichet
@ -129,3 +143,62 @@ Here is an example of Bottin ACLs that may be used to support Guichet invitation
Consult [this directory](https://git.deuxfleurs.fr/Deuxfleurs/infrastructure/src/branch/main/app/directory/config)
to view the full configuration in use on Deuxfleurs.
## Contribute & local development
Guichet needs a few components to work :
- A Bottin server
- that needs a consul server
- And a Garage cluster (of at least one node)
A basic consul / bottin stack is available through the docker compose file you can find in `integration` subdirectory:
```sh
cd integration
docker compose up -d
```
You can then run Guichet locally :
```sh
# First, copy a sample config file
copy config.json.example config.json
# Run the go development server
go run .
```
It will be available on http://localhost:9991.
### First run
#### How to get my admin password
On first Bottin's run, it is displayed in the logs.
You can easily find it by reading the container logs :
```sh
docker compose logs bottin | grep password:
```
- The **username** is provided in the log, and should look like this: `cn=admin,dc=bottin,dc=eu`.
- The **password** is right after in the same log line.
#### Garage
⚠️ Be aware at this stage that your local Guichet installation is not 100% working, especially the websites features.
You need to initialise Garage. It can be done in a few commands, coming from [the official Garage's documentation](https://garagehq.deuxfleurs.fr/documentation/quick-start/):
```sh
# Find your Garage node ID
docker compose exec garage /garage
# Your id is eb820c8da5605f78 in the output below
ID Hostname Address Tags Zone Capacity DataAvail
eb820c8da5605f78 9bd710b31be0 127.0.0.1:3901 NO ROLE ASSIGNED
# Then create a cluster layout with this id
docker compose exec garage /garage layout assign -z dc1 -c 1G eb820c8da5605f78
# Finally, apply the layout
docker compose exec garage /garage layout apply
```
🎉 You now can go to http://localhost:9991/website without getting 503 errors.

View file

@ -47,11 +47,20 @@
Entrypoint = "/bin/guichet";
};
};
docker = pkgs.writeScriptBin "guichet-docker" ''
#!/usr/bin/env bash
set -euxo pipefail
docker load <$(nix build --print-out-paths .#container)
'';
in {
packages.x86_64-linux.guichet = guichet;
packages.x86_64-linux.container = container;
packages.x86_64-linux.docker = docker;
packages.x86_64-linux.default = guichet;
devShell.x86_64-linux = pkgs.mkShell { nativeBuildInputs = [ pkgs.go pkgs.gomod2nix ]; };
};
}

2
go.sum
View file

@ -31,8 +31,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang v0.0.0-20230131081355-c965fe7f7dc9 h1:ERg8KCpIKym98EOKa8Gq0NSBxsasD3sqb/R0gg1wOzU=
git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang v0.0.0-20230131081355-c965fe7f7dc9/go.mod h1:TlSL6QVxozmdRaSgP6Akspi0HCJv4HAkkq3Dldru4GM=
git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang v0.0.0-20231128153612-8b81fae65e5e h1:h89CAh0qmUcGJykss/utXIw+yRGa3Gr6VyrZ5ZWN0kY=
git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang v0.0.0-20231128153612-8b81fae65e5e/go.mod h1:TlSL6QVxozmdRaSgP6Akspi0HCJv4HAkkq3Dldru4GM=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

View file

@ -21,7 +21,7 @@ services:
garage:
# sync with deuxfleurs/nixcfg/cluster/prod/app/garage/deploy/garage.hcl
# to ensure compatibility with prod
image: superboum/garage:v1.0.0-rc1-hotfix-red-ftr-wquorum
image: dxflrs/garage:v1.99.1-internal
ports:
- "3900:3900"
- "3902:3902"

View file

@ -1,19 +1,15 @@
package main
import (
"bytes"
"crypto/rand"
"encoding/binary"
"encoding/hex"
"fmt"
"html/template"
"log"
"net/http"
"regexp"
"strings"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
"github.com/go-ldap/ldap/v3"
"github.com/gorilla/mux"
"golang.org/x/crypto/argon2"
@ -251,21 +247,11 @@ func handleInviteSendCode(w http.ResponseWriter, r *http.Request) {
WebBaseAddress: config.WebAddress,
}
if r.Method == "POST" {
r.ParseForm()
choice := strings.Join(r.Form["choice"], "")
sendto := strings.Join(r.Form["sendto"], "")
if choice == "display" || choice == "send" {
trySendCode(user, choice, sendto, data)
}
}
tryGenerateInvitation(user, data)
templateInviteSendCode.Execute(w, data)
}
func trySendCode(user *LoggedUser, choice string, sendto string, data *SendCodeData) {
func tryGenerateInvitation(user *LoggedUser, data *SendCodeData) {
// Generate code
code, code_id, code_pw := genCode()
@ -286,43 +272,9 @@ func trySendCode(user *LoggedUser, choice string, sendto string, data *SendCodeD
return
}
// If we want to display it, do so
if choice == "display" {
data.Success = true
data.CodeDisplay = code
return
}
// Otherwise, we are sending a mail
if !EMAIL_REGEXP.MatchString(sendto) {
data.ErrorInvalidEmail = true
return
}
templateMail := template.Must(template.ParseFiles(templatePath + "/invite_mail.txt"))
buf := bytes.NewBuffer([]byte{})
templateMail.Execute(buf, &CodeMailFields{
To: sendto,
From: config.MailFrom,
InviteFrom: user.WelcomeName(),
Code: code,
WebBaseAddress: config.WebAddress,
})
log.Printf("Sending mail to: %s", sendto)
var auth sasl.Client = nil
if config.SMTPUsername != "" {
auth = sasl.NewPlainClient("", config.SMTPUsername, config.SMTPPassword)
}
err = smtp.SendMail(config.SMTPServer, auth, config.MailFrom, []string{sendto}, buf)
if err != nil {
data.ErrorMessage = err.Error()
return
}
log.Printf("Mail sent.")
data.Success = true
data.CodeSentTo = sendto
data.CodeDisplay = code
return
}
func genCode() (code string, code_id string, code_pw string) {

View file

@ -237,8 +237,6 @@ func handleLogout(w http.ResponseWriter, r *http.Request) {
// --- Login Controller ---
type LoginFormData struct {
Username string
WrongUser bool
WrongPass bool
ErrorMessage string
}
@ -266,10 +264,9 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
data := &LoginFormData{
Username: username,
}
if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) {
data.WrongPass = true
} else if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
data.WrongUser = true
if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) ||
ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
data.ErrorMessage = "Le mot de passe et identifiant ne correspondent pas."
} else {
data.ErrorMessage = err.Error()
}

View file

@ -15,7 +15,21 @@ paths:
operationId: "ListWebsites"
summary: "List all websites"
description: |
List all the user's websites
List all the user's websites.
Also includes information about global quotas for the user, and for each website, bucket names and website URL.
- `username`: the name of the user making the request.
- `quota_website_count` indicates the quota of the number of websites that the user is allowed to create.
- Website size is set at 100MB by default, and users can increase this quota autonomously up to a certain value defined by `burst_bucket_quota_size`, by default it's set to 200MB but it can be overriden on a per-user basis.
- Each element of the `vhosts` array describes a website. A website is described by:
+ `name`: current main name of the website, which determines the URL it can be accessed from (specified in `domain`). This is also a name of the S3 bucket storing the website.
+ `alt_name`: an array of aliases for the website. These can also be used to access the S3 bucket storing the website (a bucket can have several aliased names), but cannot be used in a URL to access the website.
+ `expanded`: whether the bucket name already corresponds to a full URL for the website
+ `domain`: the URL at which the website can be reached
responses:
'500':
description: |
@ -23,7 +37,6 @@ paths:
'200':
description: |
Returns information about all the user's websites, and user information related to websites.
The website quota (`quota_website_count`) indicates a quota in number of websites that the user is allowed to create. Website size is set at 100MB by default, and users can increase this quota autonomously up to a certain value defined by `burst_bucket_quota_size`, by default it's set to 200MB but it can be overriden on a per-user basis.
content:
application/json:
schema:
@ -58,10 +71,13 @@ paths:
summary: "Details on a website"
description: |
Gets the configuration and status for the website `vhost`.
This includes information about domains, bucket access keys, and quotas.
This includes information about bucket names, domains, bucket access keys, and quotas.
`quota_size` is given in bytes.
`quota_files` indicates a number of files.
- `vhost` contains informations about bucket names and the website URL. See the documentation of the response of "List all websites" for more information
- `access_key_id`: the S3 access key id for the bucket
- `secret_access_key`: the S3 secret access key for the bucket
- `quota_size`: the quota for the size of the website, in bytes.
- `quota_files`: the quota for the number of files.
responses:
'500':
description: |
@ -97,7 +113,7 @@ paths:
Request forbidden, you have reached your quota of maximum number of websites allowed.
'200':
description: |
Returns information about the website that has been created.
Returns information about the website that has been created. (See the documentation of "Details on a website" for more info.)
content:
application/json:
schema:
@ -144,7 +160,7 @@ paths:
Website not found
'200':
description: |
Returns updated information about the website
Returns updated information about the website. (See the documentation of "Details on a website" for more info.)
content:
application/json:
schema:

View file

@ -57,9 +57,9 @@
</div>
<div>
<p>Vous devez éditer votre zone DNS, souvent gérée par votre bureau d'enregistrement, comme Gandi, pour la faire pointer vers Deuxfleurs. Si vous utilisez un sous domaine (eg. <code>site.example.com</code>), une entrée <code>CNAME</code> est appropriée :</p>
<pre>site CNAME 3600 garage.deuxfleurs.fr.</pre>
<pre>site CNAME 3600 global.site.deuxfleurs.fr.</pre>
<p>Si vous utilisez la racine de votre nom de domaine (eg. <code>example.com</code>, aussi appelée APEX), la solution dépend de votre fournisseur DNS, il vous faudra au choix une entrée <code>ALIAS</code> ou <code>CNAME</code> en fonction de ce que votre fournisseur supporte :</p>
<pre>@ ALIAS 3600 garage.deuxfleurs.fr.</pre>
<pre>@ ALIAS 3600 global.site.deuxfleurs.fr.</pre>
<p>La première fois que vous chargerez votre site web, une erreur de certificat sera renvoyée. C'est normal, il faudra patienter quelques minutes le temps que le certificat se génère.</p>
</div>
<div class="mt-4">

View file

@ -42,7 +42,12 @@
</p>
</div>
<div class="col-md-9">
<h2>{{ .View.Name.Url }}</h2>
<div class="d-flex justify-content-between align-items-center">
<h2>{{ .View.Name.Url }}</h2>
<div>
<a href="https://{{ .View.Name.Url }}" target="_blank" rel="noreferrer external" class="btn btn-dark">Visiter</a>
</div>
</div>
<!-- QUOTAS -->
@ -76,7 +81,10 @@
<h5 class="mt-3">Informations de connexion</h5>
<ul class="nav nav-tabs" id="proto" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="s3-tab" data-toggle="tab" href="#s3" role="tab" aria-controls="s3" aria-selected="true">S3 (recommandé)</a>
<a class="nav-link active" id="dxfl-tab" data-toggle="tab" href="#dxfl" role="tab" aria-controls="dxfl" aria-selected="true">dxfl (recommandé)</a>
</li>
<li class="nav-item">
<a class="nav-link" id="s3-tab" data-toggle="tab" href="#s3" role="tab" aria-controls="s3" aria-selected="false">S3</a>
</li>
<li class="nav-item">
<a class="nav-link" id="sftp-tab" data-toggle="tab" href="#sftp" role="tab" aria-controls="sftp" aria-selected="false">SFTP</a>
@ -86,7 +94,20 @@
</li>
</ul>
<div class="tab-content" id="protocols">
<div class="tab-pane fade show active" id="s3" role="tabpanel" aria-labelledby="s3-tab">
<div class="tab-pane fade show active" id="dxfl" role="tabpanel" aria-labelledby="dxfl-tab">
<p>Première configuration :</p>
<pre>
sudo npm install -g dxfl
dxfl login {{ .Describe.Username }}
</pre>
<p>Pour déployer votre site contenu dans le dossier <code>public</code> :</p>
<pre>
dxfl deploy {{ .View.Name.Pretty }} ./public
</pre>
</div>
<div class="tab-pane fade show" id="s3" role="tabpanel" aria-labelledby="s3-tab">
<table class="table mt-4">
<tbody>
<tr>
@ -96,8 +117,7 @@
<tr>
<th scope="row">Clé secrète</th>
<td>
<a href="#" onclick="document.getElementById('secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a>
<span id="secret_key" style="display: none">{{ .View.SecretAccessKey }}</span>
<a href="#secret_key" onclick="document.getElementById('secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="secret_key" style="display: none">{{ .View.SecretAccessKey }}</span>
</td>
</tr>
<tr>
@ -138,7 +158,7 @@
<p>Entrez les informations suivantes quand elles vous sont demandées :</p>
<dl>
<dt>AWS Access Key ID [None]:</dt><dd>{{ .View.AccessKeyId }}</dd>
<dt>AWS Secret Access Key [None]:</dt><dd><a href="#" onclick="document.getElementById('aws_secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="aws_secret_key" style="display: none">{{ .View.SecretAccessKey }}</span></dd>
<dt>AWS Secret Access Key [None]:</dt><dd><a href="#aws_secret_key" onclick="document.getElementById('aws_secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="aws_secret_key" style="display: none">{{ .View.SecretAccessKey }}</span></dd>
<dt>Default region name [None]:</dt> <dd>garage</dd>
<dt>Default output format [None]:</dt> <dd>(laissez vide et appuyez sur entrée)</dd>
</dl>
@ -172,7 +192,7 @@ mc alias set \
{{ .View.Name.Pretty }} \
https://garage.deuxfleurs.fr \
{{ .View.AccessKeyId }} \
<a href="#" onclick="document.getElementById('minio_secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="minio_secret_key" style="display: none">{{ .View.SecretAccessKey }}</span> \
<a href="#minio_secret_key" onclick="document.getElementById('minio_secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="minio_secret_key" style="display: none">{{ .View.SecretAccessKey }}</span> \
--api S3v4
</pre>
<p>Et ensuite copiez votre site web avec la sous-commande mirror de Minio CLI :</p>
@ -196,12 +216,12 @@ mc mirror --overwrite ./public/ {{ .View.Name.Pretty }}/
<p>Créez un fichier nommé <code>.deployment.secrets</code> (ne commitez pas ce fichier dans votre dépôt !) :</p>
<pre>
export AWS_ACCESS_KEY_ID={{ .View.AccessKeyId }}
export AWS_SECRET_ACCESS_KEY=<a href="#" onclick="document.getElementById('ugo_secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="hugo_secret_key" style="display: none">{{ .View.SecretAccessKey }}</span>
export AWS_SECRET_ACCESS_KEY=<a href="#hugo_secret_key" onclick="document.getElementById('hugo_secret_key').style.display='inline'; this.style.display='none'">[Afficher la clé secrète]</a><span id="hugo_secret_key" style="display: none">{{ .View.SecretAccessKey }}</span>
</pre>
<p>Dans votre fichier de configuration Hugo <code>config.toml</code> (que vous pouvez commiter), rajoutez :</p>
<pre>
[[deployment.targets]]
URL = "s3://bucket?endpoint=garage.deuxfleurs.fr&amp;s3ForcePathStyle=true&amp;region=garage"
URL = "s3://{{ .View.Name.Pretty }}?endpoint=https://garage.deuxfleurs.fr&amp;awssdk=v2&amp;use_path_style=true&amp;region=garage&amp;disable_https=false
</pre>
<p>Pour déployer, sourcez le fichier de configuration et laissez hugo faire : </p>
@ -345,7 +365,7 @@ scp -oHostKeyAlgorithms=+ssh-rsa -P2222 -r ./public/ {{ .Describe.Username }}@sf
{{ if .View.Name.Expanded }}
<h5 class="mt-5">Vous ne savez pas comment configurer votre nom de domaine ?</h5>
<p> Le nom de domaine {{ .View.Name.Url }} n'est pas géré par Deuxfleurs, il vous revient donc de configurer la zone DNS. Vous devez ajouter une entrée <code>CNAME garage.deuxfleurs.fr</code> ou <code>ALIAS garage.deuxfleurs.fr</code> auprès de votre hébergeur DNS, qui est souvent aussi le bureau d'enregistrement (eg. Gandi, GoDaddy, BookMyName, etc.).</p>
<p> Le nom de domaine {{ .View.Name.Url }} n'est pas géré par Deuxfleurs, il vous revient donc de configurer la zone DNS. Vous devez ajouter une entrée <code>CNAME global.site.deuxfleurs.fr</code> ou <code>ALIAS global.site.deuxfleurs.fr</code> auprès de votre hébergeur DNS, qui est souvent aussi le bureau d'enregistrement (eg. Gandi, GoDaddy, BookMyName, etc.).</p>
{{ end }}

View file

@ -57,9 +57,9 @@
</div>
<div>
<p>Vous devez éditer votre zone DNS, souvent gérée par votre bureau d'enregistrement, comme Gandi, pour la faire pointer vers Deuxfleurs. Si vous utilisez un sous domaine (eg. <code>site.example.com</code>), une entrée <code>CNAME</code> est appropriée :</p>
<pre>site CNAME 3600 garage.deuxfleurs.fr.</pre>
<pre>site CNAME 3600 global.site.deuxfleurs.fr.</pre>
<p>Si vous utilisez la racine de votre nom de domaine (eg. <code>example.com</code>, aussi appelée APEX), la solution dépend de votre fournisseur DNS, il vous faudra au choix une entrée <code>ALIAS</code> ou <code>CNAME</code> en fonction de ce que votre fournisseur supporte :</p>
<pre>@ ALIAS 3600 garage.deuxfleurs.fr.</pre>
<pre>@ ALIAS 3600 global.site.deuxfleurs.fr.</pre>
<p>La première fois que vous chargerez votre site web, une erreur de certificat sera renvoyée. C'est normal, il faudra patienter quelques minutes le temps que le certificat se génère.</p>
</div>
<div class="mt-4">

View file

@ -38,8 +38,7 @@
Inviter des gens sur Deuxfleurs
</div>
<div class="list-group list-group-flush">
<a class="list-group-item list-group-item-action" href="/invite/send_code">Envoyer un code d'invitation</a>
<a class="list-group-item list-group-item-action" href="/invite/new_account">Créer un nouveau compte directement</a>
<a class="list-group-item list-group-item-action" href="/invite/send_code">Générer un code d'invitation</a>
</div>
</div>
{{end}}

View file

@ -4,15 +4,9 @@
<h4>S'identifier</h4>
<form method="POST">
{{if .WrongUser}}
<div class="alert alert-danger">Identifiant invalide.</div>
{{end}}
{{if .WrongPass}}
<div class="alert alert-danger">Mot de passe invalide.</div>
{{end}}
{{if .ErrorMessage}}
{{ with .ErrorMessage}}
<div class="alert alert-danger">Impossible de se connecter.
<div style="font-size: 0.8em">{{ .ErrorMessage }}</div>
<div style="font-size: 0.8em">{{ . }}</div>
</div>
{{end}}
<div class="form-group">