Compare commits
10 Commits
sqlite3-mu
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3576ab15d1 | ||
![]() |
78953f27f0 | ||
![]() |
002d0e6309 | ||
![]() |
b8ce944b5c | ||
![]() |
7bc873580c | ||
![]() |
96c197453d | ||
![]() |
561568343a | ||
![]() |
c996ae1cad | ||
![]() |
393f6d6834 | ||
![]() |
bbed72ff6b |
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Let us know what went wrong.
|
||||
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
Explain what the bug is, in as much detail as possible...
|
||||
|
||||
### Steps to reproduce (if necessary)
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. ...
|
||||
|
||||
### Expected behavior
|
||||
What should've happened?
|
||||
|
||||
### Application configuration
|
||||
- **Single mode or Multi-user mode?**
|
||||
- **Open registration?** [yes/no]
|
||||
- **Federation enabled?** [yes/no]
|
||||
|
||||
**Version or last commit**:
|
9
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
9
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
# PLEASE DON'T SUBMIT FEATURE REQUESTS HERE #
|
||||
|
||||
Instead, post them to our forums: https://discuss.write.as/c/feedback/feature-requests
|
4
Makefile
4
Makefile
@@ -20,8 +20,8 @@ run:
|
||||
deps :
|
||||
$(GOGET) -v ./...
|
||||
|
||||
install :
|
||||
./keys.sh
|
||||
install : build
|
||||
cmd/writefreely/$(BINARY_NAME) --gen-keys
|
||||
cd less/; $(MAKE) install $(MFLAGS)
|
||||
|
||||
ui : force_look
|
||||
|
13
README.md
13
README.md
@@ -1,6 +1,6 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://writefreely.org"><img src="https://writefreely.org/writefreely.svg" width="350px" alt="Write Freely" /></a>
|
||||
<a href="https://writefreely.org"><img src="https://writefreely.org/img/writefreely.svg" width="350px" alt="Write Freely" /></a>
|
||||
</p>
|
||||
<hr />
|
||||
<p align="center">
|
||||
@@ -13,6 +13,9 @@
|
||||
<a href="https://travis-ci.org/writeas/writefreely">
|
||||
<img src="https://travis-ci.org/writeas/writefreely.svg" alt="Build status" />
|
||||
</a>
|
||||
<a href="http://webchat.freenode.net/?channels=writefreely">
|
||||
<img alt="#writefreely on freenode" src="https://img.shields.io/badge/freenode-%23writefreely-blue.svg" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -52,8 +55,8 @@ mysql -u YOURUSERNAME -p writefreely < schema.sql
|
||||
# 3) Configure your blog
|
||||
./writefreely --config
|
||||
|
||||
# 4) Generate data encryption keys (especially for production)
|
||||
./keys.sh
|
||||
# 4) Generate data encryption keys
|
||||
./writefreely --gen-keys
|
||||
|
||||
# 5) Run
|
||||
./writefreely
|
||||
@@ -79,9 +82,7 @@ Ready to hack on your site? Here's a quick overview.
|
||||
go get github.com/writeas/writefreely/cmd/writefreely
|
||||
```
|
||||
|
||||
Create your database, import the schema, and configure your site [as shown above](#quick-start).
|
||||
|
||||
Now generate the CSS:
|
||||
Create your database, import the schema, and configure your site [as shown above](#quick-start). Then generate the remaining files you'll need:
|
||||
|
||||
```bash
|
||||
make install # Generates encryption keys; installs LESS compiler
|
||||
|
@@ -255,6 +255,16 @@ func handleFetchCollectionInbox(app *app, w http.ResponseWriter, r *http.Request
|
||||
b, _ := json.Marshal(m)
|
||||
log.Info("Follow: %s", b)
|
||||
|
||||
_, followID := f.GetId()
|
||||
if followID == nil {
|
||||
log.Error("Didn't resolve follow ID")
|
||||
} else {
|
||||
acceptID, err := url.Parse(followID.String() + "-accept")
|
||||
if err != nil {
|
||||
log.Error("Couldn't parse generated Accept URL '%s': %v", followID.String()+"#accept", err)
|
||||
}
|
||||
a.SetId(acceptID)
|
||||
}
|
||||
a.AppendObject(f.Raw())
|
||||
_, to = f.GetActor(0)
|
||||
obj := f.Raw().GetObjectIRI(0)
|
||||
@@ -580,6 +590,8 @@ func federatePost(app *app, p *PublicPost, collID int64, isUpdate bool) error {
|
||||
activity = activitystreams.NewUpdateActivity(na)
|
||||
} else {
|
||||
activity = activitystreams.NewCreateActivity(na)
|
||||
activity.To = na.To
|
||||
activity.CC = na.CC
|
||||
}
|
||||
err = makeActivityPost(actor, si, activity)
|
||||
if err != nil {
|
||||
|
21
app.go
21
app.go
@@ -29,7 +29,8 @@ const (
|
||||
|
||||
serverSoftware = "WriteFreely"
|
||||
softwareURL = "https://writefreely.org"
|
||||
softwareVer = "0.1"
|
||||
|
||||
softwareVer = "0.2"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -124,6 +125,7 @@ func Serve() {
|
||||
debugPtr := flag.Bool("debug", false, "Enables debug logging.")
|
||||
createConfig := flag.Bool("create-config", false, "Creates a basic configuration and exits")
|
||||
doConfig := flag.Bool("config", false, "Run the configuration process")
|
||||
genKeys := flag.Bool("gen-keys", false, "Generate encryption and authentication keys")
|
||||
flag.Parse()
|
||||
|
||||
debugging = *debugPtr
|
||||
@@ -167,6 +169,23 @@ func Serve() {
|
||||
log.Info("Done!")
|
||||
}
|
||||
os.Exit(0)
|
||||
} else if *genKeys {
|
||||
errStatus := 0
|
||||
|
||||
err := generateKey(emailKeyPath)
|
||||
if err != nil {
|
||||
errStatus = 1
|
||||
}
|
||||
err = generateKey(cookieAuthKeyPath)
|
||||
if err != nil {
|
||||
errStatus = 1
|
||||
}
|
||||
err = generateKey(cookieKeyPath)
|
||||
if err != nil {
|
||||
errStatus = 1
|
||||
}
|
||||
|
||||
os.Exit(errStatus)
|
||||
}
|
||||
|
||||
log.Info("Initializing...")
|
||||
|
@@ -264,12 +264,11 @@ func (c *Collection) PersonObject(ids ...int64) *activitystreams.Person {
|
||||
p.Name = c.DisplayTitle()
|
||||
p.Summary = c.Description
|
||||
if p.Name != "" {
|
||||
fl := string(unicode.ToLower([]rune(p.Name)[0]))
|
||||
if isLowerLetter(fl) {
|
||||
if av := c.AvatarURL(); av != "" {
|
||||
p.Icon = activitystreams.Image{
|
||||
Type: "Image",
|
||||
MediaType: "image/png",
|
||||
URL: hostName + "/img/avatars/" + fl + ".png",
|
||||
URL: av,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,6 +286,14 @@ func (c *Collection) PersonObject(ids ...int64) *activitystreams.Person {
|
||||
return p
|
||||
}
|
||||
|
||||
func (c *Collection) AvatarURL() string {
|
||||
fl := string(unicode.ToLower([]rune(c.DisplayTitle())[0]))
|
||||
if !isLowerLetter(fl) {
|
||||
return ""
|
||||
}
|
||||
return hostName + "/img/avatars/" + fl + ".png"
|
||||
}
|
||||
|
||||
func (c *Collection) FederatedAPIBase() string {
|
||||
return hostName + "/"
|
||||
}
|
||||
|
58
keys.go
58
keys.go
@@ -1,7 +1,23 @@
|
||||
package writefreely
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"github.com/writeas/web-core/log"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
keysDir = "keys"
|
||||
|
||||
encKeysBytes = 32
|
||||
)
|
||||
|
||||
var (
|
||||
emailKeyPath = filepath.Join(keysDir, "email.aes256")
|
||||
cookieAuthKeyPath = filepath.Join(keysDir, "cookies_auth.aes256")
|
||||
cookieKeyPath = filepath.Join(keysDir, "cookies_enc.aes256")
|
||||
)
|
||||
|
||||
type keychain struct {
|
||||
@@ -12,20 +28,56 @@ func initKeys(app *app) error {
|
||||
var err error
|
||||
app.keys = &keychain{}
|
||||
|
||||
app.keys.emailKey, err = ioutil.ReadFile("keys/email.aes256")
|
||||
app.keys.emailKey, err = ioutil.ReadFile(emailKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.keys.cookieAuthKey, err = ioutil.ReadFile("keys/cookies_auth.aes256")
|
||||
app.keys.cookieAuthKey, err = ioutil.ReadFile(cookieAuthKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.keys.cookieKey, err = ioutil.ReadFile("keys/cookies_enc.aes256")
|
||||
app.keys.cookieKey, err = ioutil.ReadFile(cookieKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateKey generates a key at the given path used for the encryption of
|
||||
// certain user data. Because user data becomes unrecoverable without these
|
||||
// keys, this won't overwrite any existing key, and instead outputs a message.
|
||||
func generateKey(path string) error {
|
||||
// Check if key file exists
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
log.Info("%s already exists. rm the file if you understand the consquences.", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("Generating %s.", path)
|
||||
b, err := generateBytes(encKeysBytes)
|
||||
if err != nil {
|
||||
log.Error("FAILED. %s. Run writefreely --gen-keys again.", err)
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(path, b, 0600)
|
||||
if err != nil {
|
||||
log.Error("FAILED writing file: %s", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Success.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateBytes returns securely generated random bytes.
|
||||
func generateBytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
25
keys.sh
25
keys.sh
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# keys.sh generates keys used for the encryption of certain user data. Because
|
||||
# user data becomes unrecoverable without these keys, the script and won't
|
||||
# overwrite any existing keys unless you explicitly delete them.
|
||||
#
|
||||
|
||||
# Generate cookie encryption and authentication keys
|
||||
if [[ ! -e "$(pwd)/keys/cookies_enc.aes256" ]]; then
|
||||
dd of=$(pwd)/keys/cookies_enc.aes256 if=/dev/urandom bs=32 count=1
|
||||
else
|
||||
echo "cookies key already exists! rm keys/cookies_enc.aes256 if you understand the consquences."
|
||||
fi
|
||||
if [[ ! -e "$(pwd)/keys/cookies_auth.aes256" ]]; then
|
||||
dd of=$(pwd)/keys/cookies_auth.aes256 if=/dev/urandom bs=32 count=1
|
||||
else
|
||||
echo "cookies authentication key already exists! rm keys/cookies_auth.aes256 if you understand the consquences."
|
||||
fi
|
||||
|
||||
# Generate email encryption key
|
||||
if [[ ! -e "$(pwd)/keys/email.aes256" ]]; then
|
||||
dd of=$(pwd)/keys/email.aes256 if=/dev/urandom bs=32 count=1
|
||||
else
|
||||
echo "email key already exists! rm keys/email.aes256 if you understand the consquences."
|
||||
fi
|
@@ -1,4 +1,4 @@
|
||||
Keys
|
||||
====
|
||||
|
||||
Contains keys for encrypting database and session data. Generate necessary keys by running (from the root of the project) `./keys.sh`.
|
||||
Contains keys for encrypting database and session data. Generate necessary keys by running (from the root of the project) `writefreely --gen-keys`.
|
||||
|
@@ -156,7 +156,6 @@ CREATE TABLE IF NOT EXISTS `remoteusers` (
|
||||
`actor_id` varchar(255) NOT NULL,
|
||||
`inbox` varchar(255) NOT NULL,
|
||||
`shared_inbox` varchar(255) NOT NULL,
|
||||
`followers` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `collection_id` (`actor_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
@@ -20,14 +20,14 @@
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:description" content="{{.Summary}}">
|
||||
<meta name="twitter:title" content="{{.PlainDisplayTitle}} — {{if .Collection.Title}}{{.Collection.Title}}{{else}}{{.Collection.Alias}}{{end}}">
|
||||
{{if gt (len .Images) 0}}<meta name="twitter:image" content="{{index .Images 0}}">{{else}}<meta name="twitter:image" content="https://write.as/img/w-sq-light.png">{{end}}
|
||||
{{if gt (len .Images) 0}}<meta name="twitter:image" content="{{index .Images 0}}">{{else}}<meta name="twitter:image" content="{{.Collection.AvatarURL}}">{{end}}
|
||||
<meta property="og:title" content="{{.PlainDisplayTitle}}" />
|
||||
<meta property="og:description" content="{{.Summary}}" />
|
||||
<meta property="og:site_name" content="{{.Collection.DisplayTitle}}" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="{{.CanonicalURL}}" />
|
||||
<meta property="og:updated_time" content="{{.Created8601}}" />
|
||||
{{range .Images}}<meta property="og:image" content="{{.}}" />{{else}}<meta property="og:image" content="https://write.as/img/w-sq-light.png">{{end}}
|
||||
{{range .Images}}<meta property="og:image" content="{{.}}" />{{else}}<meta property="og:image" content="{{.Collection.AvatarURL}}">{{end}}
|
||||
<meta property="article:published_time" content="{{.Created8601}}">
|
||||
{{if .Collection.StyleSheet}}<style type="text/css">{{.Collection.StyleSheetDisplay}}</style>{{end}}
|
||||
{{if .Collection.RenderMathJax}}
|
||||
|
@@ -23,10 +23,12 @@
|
||||
<meta name="twitter:site" content="@writeas__">
|
||||
<meta name="twitter:description" content="{{.Tag}} posts on {{.Collection.DisplayTitle}}">
|
||||
<meta name="twitter:title" content="{{.Tag}} — {{.Collection.DisplayTitle}}">
|
||||
<meta name="twitter:image" content="{{.Collection.AvatarURL}}">
|
||||
<meta property="og:title" content="{{.Tag}} — {{.Collection.DisplayTitle}}" />
|
||||
<meta property="og:site_name" content="{{.DisplayTitle}}" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="{{.CanonicalURL}}tag:{{.Tag}}" />
|
||||
<meta property="og:image" content="{{.Collection.AvatarURL}}">
|
||||
{{if .Collection.StyleSheet}}<style type="text/css">{{.Collection.StyleSheetDisplay}}</style>{{end}}
|
||||
{{if .Collection.RenderMathJax}}
|
||||
<script type="text/x-mathjax-config">
|
||||
|
@@ -19,12 +19,14 @@
|
||||
<meta itemprop="description" content="{{.Description}}">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="{{.DisplayTitle}}">
|
||||
<meta name="twitter:image" content="{{.AvatarURL}}">
|
||||
<meta name="twitter:description" content="{{.Description}}">
|
||||
<meta property="og:title" content="{{.DisplayTitle}}" />
|
||||
<meta property="og:site_name" content="{{.DisplayTitle}}" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="{{.CanonicalURL}}" />
|
||||
<meta property="og:description" content="{{.Description}}" />
|
||||
<meta property="og:image" content="{{.AvatarURL}}">
|
||||
{{if .StyleSheet}}<style type="text/css">{{.StyleSheetDisplay}}</style>{{end}}
|
||||
{{if .RenderMathJax}}
|
||||
<script type="text/x-mathjax-config">
|
||||
|
Reference in New Issue
Block a user