.chglog | ||
.github | ||
Wave | ||
.dockerignore | ||
.gitattributes | ||
.gitignore | ||
docker-compose.dcproj | ||
docker-compose.override.yml | ||
docker-compose.yml | ||
launchSettings.json | ||
LICENSE | ||
README.md | ||
Wave.sln |
Wave - The Collaborative Open-Source Blogging Engine
Stay afloat in a current of Information
⚠ Under Construction ⚠
Quickstart
This docker compose file will give you everything you need to run Wave. See the following sections for explanations about the configuration and makeup of Wave. Replace <*_password> with generated passwords, just in case, replace your-time-zone with a sensible time zone for your users.
For extensive configuration you want to mount /configuration
to a location on your system.
Afterwards you can access Wave on http://localhost
.
To see how to create an admin account, read the following section. Afterwards for security you should Configure an Email Server.
version: '3.4'
name: wave
services:
web:
image: miawinter/wave:latest
restart: unless-stopped
ports:
- "80:8080"
links:
- database:db
environment:
- "TZ=<your-time-zone>"
- "WAVE_ConnectionStrings__DefaultConnection=Host=db; Username=wave; Password=<db_password>"
- "WAVE_ConnectionStrings__Redis=redis,password=<redis_password>"
volumes:
- wave-files:/app/files
- wave-config:/configuration
networks:
- wave
depends_on:
- database
database:
image: postgres:16.1-alpine
restart: unless-stopped
environment:
- "POSTGRES_DB=wave"
- "POSTGRES_USER=wave"
- "POSTGRES_PASSWORD=<db_password>"
volumes:
- wave-db:/var/lib/postgresql/data
networks:
- wave
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass <redis_password> --save 60 1 --loglevel warning
volumes:
- wave-redis:/data
networks:
- wave
volumes:
wave-files:
wave-config:
wave-db:
wave-redis:
networks:
wave:
Note: when binding the files volume to a local directory, keep in mind Wave runs by default on an
internal "app" user (1654:1654
), not root. You need to adjust your directory permissions or
docker compose file accordingly (or just slap chmod -R 777
on it).
Admin Access
When Wave does not detect any admin account in its database on startup, which usually happens during
setup, a message will be printed to it's server console, in docker accessible with docker logs wave-web-1
:
There is currently no user in your installation with the admin role, go to /Admin and use the following password to self promote your account: [password]
The password is 16 digits long, navigate to http://localhost/Admin
, if you are not logged in you will be redirected to
the login page. Once you are authenticated and have entered the password on the admin page, the tool will be disabled and
you will be a member of the admin role, giving you full access to all of Waves' features. Keep in mind that the password
is generated every time on startup as long as there is no admin, so if you restart the container, there will be a different
password in the console. Since pull request #3 probably stays the same.
Configuring Wave
Wave allows you to configure it in many different formats and in multiple places, and you can even use multiple of the following methods to supply configuration information. Please keep in mind that first, asp.net configuration keys are case-insensitive, and second, that there is a precedence in the different formats, so a value for the same key in two formats will be overwritten by one.
Configuration Locations
There are two main locations where Wave (and asp.net) takes its configuration from:
The Environment, and the /configuration
volume. Environment variables allow you to quickly
set up a docker container, but the more you need to configure the more unmaintainable an
.env
file (or an environment:
section in docker compose) becomes, so if you find yourself
customizing a lot of Waves behaviour, consider using one of the many supported configuration
file formats.
Configuration Keys
I will provide you the different configuration keys with a dot notation, like Email.Smtp.Host
.
In environment variables, these dots need to be replaced with two underscore characters: __
and prefixed with WAVE_
.
In config files, those dots are hierarchy level, and you need to implement that dialects'
syntax for it. Here some examples for Email.Smtp.Host
:
Environment
WAVE_Email__Smtp__Host=smtp.example.com
JSON
{
"Email": {
"Smtp:": {
"Host": "smtp.example.com"
}
}
}
YAML
Email:
Smtp:
Host: smtp.example.com
Supported Configuration Formats
Wave will take configuration from the following files in the /configuration
volume, files
later in this chain will have precedence over files earlier in that chain:
- config.json
- config.yml
- config.toml
- config.ini
- config.xml
After this, values from the Environment will take the highest precedence.
Configuring Email
Wave may send user related mails every now and then, to confirm an account, reset a password, etc. In order to support that, Wave needs to have a way to send Emails, currently SMTP is supported.
SMTP
The following configuration is required for Wave to connect to a smtp server (formatted in YAML for brevity).
Email:
SenderEmail: noreply@example.com
SenderName: Wave
ServiceEmail: contact@example.com # used in various places, including email newsletter ListId Header
Smtp:
Live:
Host: smtp.example.com
Port: 25
Username: user
Password: password
Ssl: true
Username
and Password
are optional if your server does not require it, and Ssl
is
true
by default, only set it to false if you really need to, keeping security in mind.
Redis
Wave will generate a variety of keys for anti-forgery and logged in users during it's
runtime. By default, these will be persisted into an in-memory key store, which will be
lost when restarting the Wave container, causing all users to be logged out. To persist
these keys outside of the containers' lifetime, you can configure a Redis connection string
using ConnectionStrings.Redis
.
Reverse Proxy
In order to make your Wave installation available to the web, you want to use a reverse proxy to handle things like SSL Certificates. Here are some examples.
Caddy
In the Caddyfile add:
<your domain> {
encode gzip zstd
@static path_regexp \.(js|css|svg|png|webp|jxl|jpg|jpeg|woff2)$
header ?Cache-Control "max-age=600"
header @static Cache-Control "max-age=3600"
header -server
reverse_proxy localhost:8080
}
If Caddy runs as a docker container, you need to use the Wave container name instead of localhost
.
Adapt the Cache-Control header to your needs, this config sets a fallback of 600 (10 minutes) and
3600 (60 minutes) for static assets like fonts, images, js and css. Wave will set a Cache-Control
header for certain files, only overwrite them if you know what you are doing.
Nginx
TODO
RSS
Wave by default supports retrieving your articles with rss, both the vanilla and the atom flavour.
The RSS endpoints are located at /rss/rss.xml
and /rss/atom.xml
. They also allow you to filter for
a category with a category
query parameter like so: /rss/atom.xml?category=cats
.
If for some reason you wish to disable these endpoints and RSS with it, set the configuration key
Features.RSS
to false. Please don't disable it because you think it will make your site more secure
or less scrapable, it does not.
Email Subscriptions
Wave can allow users to subscribe to E-Mail updates about new articles. In order to enable this feature
you first need to set the configuration key Features.EmailSubscriptions
to true. You also need to configure
Emails in general, please follow the Configure an Email Server Section for this.
Besides this you will need to provide a mail distributor for these bulk E-Mails. This may be the same as you have already configured for live E-Mails, in this case just copy the configuration from there, but especially at larger volumes it is advisable to separate these concerns, and many hosting providers for large mail distribution will provide you with two sets of credentials for this.
If you have followed all the previous instructions, your E-Mail configuration may look like this:
Email:
SenderEmail: noreply@example.com
SenderName: Wave
ServiceEmail: contact@example.com # used in various places, including email newsletter ListId Header
Smtp:
Live:
Host: smtp.example.com
Port: 25
Username: user
Password: password
Ssl: true
Bulk:
Host: bulk.smtp.example.com
Port: 465
Username: bulk-user
Password: bulk-password
Customizations
TODO implement more customizations, add description.
Currently supported:
Customization:
AppName: My cool blog
AppDescription: This is where I write about my cats
AppUrl: https://example.com
DefaultTheme: wave-dark
DefaultLanguage: "en-GB"
LogoLink: https://miawinter.de/img/logo.png
Footer: (c) 2024 [Mia Rose Winter](https://miawinter.de/)
Featured Article Embeds
Wave allows access to the most recent article via the api endpoint /api/article/featured
.
This returns a json object with data about it, but there is also a script /featured.js
that
requests this automatically and inserts it into the page.
The simplest embed in your page looks like this:
<body>
<!-- ... -->
<div data-wave data-wave-pfp-size="150">
Loading featured article...
</div>
<script id="wave-script" src="<your-wave-url>/featured.js" defer></script>
</body>
data-wave-pfp-size
may be omitted, the default is 150. Wave will inject a default styled
hero into the div with the data tag data-wave
. If you want to provide your own template,
you can use an html template:
<template data-wave-template>
<div style="padding: 1em; border: 1px solid black; box-shadow: 4px 4px 0 0 currentColor; background: #ffb3c8;">
<h1 data-wave-title style="margin: 0 0 0.5em 0"></h1>
<img style="float: left; margin: 0 0.5em 0.5em 0; border: 1px solid transparent; border-radius: 0.25em"
data-wave-author-profilePictureUrl alt="" width="${pfpSize}" />
<p style="line-height: 1.4em; margin: 0">
<a data-wave-author-profileUrl target="_blank" style="text-decoration: none; color: black">
<small data-wave-author-name style="font-weight: bold"></small><br>
</a>
<small data-wave-publishDate></small><br>
<span data-wave-contentPreview></span><br>
<a data-wave-browserUrl target="_blank" style="color: black">Read More</a>
</p>
</div>
</template>
This is the defautl template by the way. Any property in the json response of /api/article/featured
corresponds to
a data-wave-*
attribute. With author
and reviewer
having their own nested tags as data-wave-author-*
and
data-wave-review-*
respectively.
So to get the name of the article author, one could do this:
<span data-wave-author-name></span>
Values will always be inserted with innerText
, except if the target tag is a link, then href
is used,
or an image tag, then src
is used.
OpenID Connect
You can configure OIDC for users to be able to log in with a OAauth2 / OpenID provider of your choice.
Keep in mind that users will still need to provide and confirm an email address, as Wave requires it for account management, and users also have the option to set a local password as an alternative, as well as to remove that OpenID Service from their account after they did so.
Oidc:
Authority: <authority base url>
ClientId: <id>
ClientSecret: <secret>
The Authority URL is usually the base url to the OpenID api, for example for Microsoft Azure/Entra/whatever they
will call it next month it's https://login.microsoftonline.com/<tenant_id>/v2.0
.
If your providers response contains an "email" property, that email will be automatically filled in on registration,
which is convenient when your users already use it to sign in.
If your providers response contains a "roles" property, that one will be treated as one or more roles (comma separated)
that that user should be assigned at signup. The available roles right now are Author
, Reviewer
, Moderator
and Admin
.
Additional Notes
TODO ?
License and Attribution
Wave by Mia Winter is licensed under the MIT License.
Copyright (c) 2024 Mia Rose Winter