b7bee75e1f
* With the new JWT signatures on photo URLs, it was no longer possible for creative users to embed their gallery photos on their profile page. * Add a function to ReSignPhotoLinks that finds/replaces (on the server side) all references to paths under "/static/photos/" and gives them a fresh ?jwt= query string signature. * Note: only applies to the profile page essays, ReSignPhotoLinks is a template func that must be opted-in on a per page basis. Other miscellaneous fixes * Add "Edit" buttons in the corners of profile cards, when the current user looks at their profile page. They link to URIs like "/settings#profile/about_me" which will now: 1. Select the "Profile settings" tab like #profile 2. Scroll and focus the profile essay field that the user clicked to edit.
198 lines
6.5 KiB
Go
198 lines
6.5 KiB
Go
// Package config holds some (mostly static) configuration for the app.
|
|
package config
|
|
|
|
import (
|
|
"regexp"
|
|
"time"
|
|
)
|
|
|
|
// Branding
|
|
const (
|
|
Title = "nonshy"
|
|
Subtitle = "A social network for nudists and exhibitionists."
|
|
)
|
|
|
|
// Paths and layouts
|
|
const (
|
|
TemplatePath = "./web/templates"
|
|
StaticPath = "./web/static"
|
|
SettingsPath = "./settings.json"
|
|
|
|
// Web path where photos are kept. Photos in DB store only their filenames, this
|
|
// is the base URL that goes in front. TODO: support setting a CDN URL prefix.
|
|
JpegQuality = 90
|
|
PhotoWebPath = "/static/photos"
|
|
PhotoDiskPath = "./web/static/photos"
|
|
)
|
|
|
|
// PhotoURLRegexp describes an image path under "/static/photos" that can be parsed from Markdown or HTML input.
|
|
// It is used by e.g. the ReSignURLs function - if you move image URLs to a CDN this may need updating.
|
|
var PhotoURLRegexp = regexp.MustCompile(`(?:['"])(/static/photos/[^'"\s?]+(?:\?[^'"\s]*)?)(?:['"]|[^'"\s]*)`)
|
|
|
|
// Security
|
|
const (
|
|
BcryptCost = 14
|
|
SessionCookieName = "session_id"
|
|
CSRFCookieName = "xsrf_token"
|
|
CSRFInputName = "_csrf" // html input name
|
|
SessionCookieMaxAge = 60 * 60 * 24 * 30
|
|
SessionRedisKeyFormat = "session/%s"
|
|
MaxBodySize = 1024 * 1024 * 8 // max upload file (e.g., 8 MB gifs)
|
|
MultipartMaxMemory = 1024 * 1024 * 1024 * 20 // 20 MB
|
|
|
|
TwoFactorBackupCodeCount = 12
|
|
TwoFactorBackupCodeLength = 8 // characters a-z0-9
|
|
|
|
// Signed URLs for static photo authentication.
|
|
SignedPhotoJWTExpires = 30 * time.Second // Regular, per-user, short window
|
|
SignedPublicAvatarJWTExpires = 7 * 24 * time.Hour // Widely public, e.g. chat room
|
|
SignedPublicAvatarUsername = "@" // JWT 'username' for widely public JWT
|
|
)
|
|
|
|
// Authentication
|
|
const (
|
|
// Skip the email verification step. The signup page will directly ask for
|
|
// email+username+password rather than only email and needing verification.
|
|
SkipEmailVerification = false
|
|
|
|
SignupTokenRedisKey = "signup-token/%s"
|
|
ResetPasswordRedisKey = "reset-password/%s"
|
|
ChangeEmailRedisKey = "change-email/%s"
|
|
SignupTokenExpires = 24 * time.Hour // used for all tokens so far
|
|
|
|
// How to rate limit same types of emails being delivered, e.g.
|
|
// signups, cert approvals (double post), etc.
|
|
EmailDebounceDefault = 24 * time.Hour // default debounce per type of email
|
|
EmailDebounceResetPassword = 4 * time.Hour // "forgot password" emails debounce
|
|
|
|
// Rate limits
|
|
RateLimitRedisKey = "rate-limit/%s/%s" // namespace, id
|
|
LoginRateLimitWindow = 1 * time.Hour
|
|
LoginRateLimit = 10 // 10 failed login attempts = locked for full hour
|
|
LoginRateLimitCooldownAt = 3 // 3 failed attempts = start throttling
|
|
LoginRateLimitCooldown = 30 * time.Second
|
|
|
|
// Contact form rate limits for logged-out users to curb spam robots:
|
|
// - One message can be submitted every 2 minutes
|
|
// - If they post 10 minutes in an hour they are paused for one hour.
|
|
ContactRateLimitWindow = 1 * time.Hour
|
|
ContactRateLimit = 10
|
|
ContactRateLimitCooldownAt = 1
|
|
ContactRateLimitCooldown = 2 * time.Minute
|
|
|
|
// "Mark Explicit" rate limit to curb a mischievous user just bulk marking the
|
|
// whole gallery as explicit.
|
|
MarkExplicitRateLimitWindow = 1 * time.Hour
|
|
MarkExplicitRateLimit = 20 // 10 failed MarkExplicit attempts = locked for full hour
|
|
MarkExplicitRateLimitCooldownAt = 10 // 10 photos in an hour, start throttling.
|
|
MarkExplicitRateLimitCooldown = time.Minute
|
|
|
|
// How frequently to refresh LastLoginAt since sessions are long-lived.
|
|
LastLoginAtCooldown = time.Hour
|
|
|
|
// Chat room status refresh interval.
|
|
ChatStatusRefreshInterval = 30 * time.Second
|
|
|
|
// Cache TTL for the demographics page.
|
|
DemographicsCacheTTL = time.Hour
|
|
)
|
|
|
|
var (
|
|
UsernameRegexp = regexp.MustCompile(`^[a-z0-9_.-]{3,32}$`)
|
|
ReservedUsernames = []string{
|
|
"admin",
|
|
"admins",
|
|
"administrator",
|
|
"moderator",
|
|
"support",
|
|
"staff",
|
|
"nonshy",
|
|
"here",
|
|
"all",
|
|
"everyone",
|
|
"everybody",
|
|
}
|
|
)
|
|
|
|
// Photo Galleries
|
|
const (
|
|
MaxPhotoWidth = 1280
|
|
ProfilePhotoWidth = 512
|
|
AltTextMaxLength = 5000
|
|
|
|
// Quotas for uploaded photos.
|
|
PhotoQuotaUncertified = 6
|
|
PhotoQuotaCertified = 100
|
|
|
|
// Rate limit for too many Site Gallery pictures.
|
|
// Some users sign up and immediately max out their gallery and spam
|
|
// the Site Gallery page. These limits can ensure only a few Site Gallery
|
|
// pictures can be posted per day.
|
|
SiteGalleryRateLimitMax = 5
|
|
SiteGalleryRateLimitInterval = 24 * time.Hour
|
|
|
|
// Only ++ the Views count per user per photo within a small
|
|
// window of time - if a user keeps reloading the same photo
|
|
// rapidly it does not increment the view counter more.
|
|
PhotoViewDebounceRedisKey = "debounce-view/user=%d/photoid=%d"
|
|
PhotoViewDebounceCooldown = 1 * time.Hour
|
|
)
|
|
|
|
// Forum settings
|
|
const (
|
|
// Only ++ the Views count per user per thread within a small
|
|
// window of time - if a user keeps reloading the same thread
|
|
// rapidly it does not increment the view counter more.
|
|
ThreadViewDebounceRedisKey = "debounce-view/user=%d/thr=%d"
|
|
ThreadViewDebounceCooldown = 1 * time.Hour
|
|
|
|
// Enable user-owned forums (feature flag)
|
|
UserForumsEnabled = true
|
|
)
|
|
|
|
// User-Owned Forums: Quota settings for how many forums a user can own.
|
|
var (
|
|
// They get one forum after they've been Certified for 45 days.
|
|
UserForumQuotaCertLifetimeDays = time.Hour * 24 * 45
|
|
|
|
// Schedule for gaining additional quota for a number of comments written
|
|
// on any forum thread. The user must have the sum of all of these post
|
|
// counts to gain one forum per level.
|
|
UserForumQuotaCommentCountSchedule = []int64{
|
|
10, // Get a forum after your first 10 posts.
|
|
20, // Get a 2nd forum after 20 additional posts (30 total)
|
|
30, // 30 more posts (60 total)
|
|
60, // 60 more posts (120 total)
|
|
80, // 80 more posts (200 total)
|
|
100, // and then one new forum for every 100 additional posts
|
|
}
|
|
)
|
|
|
|
// Poll settings
|
|
var (
|
|
// Max number of responses to accept for a poll (how many form
|
|
// values the app will read in). NOTE: also enforced in frontend
|
|
// UX in new_post.html, update there if you change this.
|
|
PollMaxAnswers = 100
|
|
|
|
// Poll color CSS classes (Bulma). Plugged in to templates like:
|
|
// <progress class="$CLASS">
|
|
// Values will wrap around for long polls.
|
|
PollProgressBarClasses = []string{
|
|
"progress is-success",
|
|
"progress is-link",
|
|
"progress is-warning",
|
|
"progress is-danger",
|
|
"progress is-primary",
|
|
"progress is-info",
|
|
}
|
|
)
|
|
|
|
// Variables set by main.go to make them readily available.
|
|
var (
|
|
RuntimeVersion string
|
|
RuntimeBuild string
|
|
RuntimeBuildDate string
|
|
Debug bool // app is in debug mode
|
|
)
|