Add content policy and add abuse categories
This commit is contained in:
25
main.go
25
main.go
@@ -2,8 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"fornaxian.tech/pixeldrain_server/util"
|
||||
web "fornaxian.tech/pixeldrain_web/init"
|
||||
|
||||
"github.com/Fornaxian/log"
|
||||
@@ -14,17 +16,32 @@ import (
|
||||
// be directly embedded by another Go project. And when deployed it will run
|
||||
// independently.
|
||||
func main() {
|
||||
var err error
|
||||
var sock = flag.Bool("systemd-socket", false, "Enable/disable systemd socket activation")
|
||||
var listen = flag.String("listen", ":8081", "The address which the API server will listen on")
|
||||
var prefix = flag.String("prefix", "", "Prefix that comes before the API URL")
|
||||
flag.Parse()
|
||||
|
||||
r := httprouter.New()
|
||||
var listener net.Listener
|
||||
|
||||
web.Init(r, *prefix, true)
|
||||
// Serve the API on a socket. If systemd-socket is enabled we'll reuse
|
||||
// systemd's socket, else we'll create our own to serve on
|
||||
if *sock {
|
||||
// Socket activation enabled. Get the provided sockets and serve on them
|
||||
if listener, err = util.SystemdSocketByName("pd-web.socket"); err != nil {
|
||||
panic("Socket pd-web.socket not found")
|
||||
}
|
||||
} else {
|
||||
// Socket activation disabled, so we create our own listener to serve on
|
||||
if listener, err = net.Listen("tcp", *listen); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
err := http.ListenAndServe(*listen, r)
|
||||
var router = httprouter.New()
|
||||
web.Init(router, *prefix, true)
|
||||
|
||||
if err != nil {
|
||||
if err = http.Serve(listener, router); err != nil {
|
||||
log.Error("Can't listen and serve Pixeldrain Web: %v", err)
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,33 @@ spare some coins. Possible methods for donating are:
|
||||
* <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WU49A5NHPAZ9G&source=url">
|
||||
Donate with PayPal</a>
|
||||
|
||||
## Content policy
|
||||
|
||||
The following types of content are not allowed to be shared on pixeldrain. They
|
||||
will be removed when reported.
|
||||
|
||||
* **Copyright violation**: Works which are shared without permission from the
|
||||
copyright holder.
|
||||
* **Abuse of minors**: Videos, images or audio fragments depicting abuse or
|
||||
inappropriate touching of minors will be removed and reported to the National
|
||||
Center for Missing and Exploited Children when found.
|
||||
* **Terrorism**: Videos, images or audio fragments which promote and
|
||||
glorify acts of terrorism.
|
||||
* **Gore**: Graphic and shocking videos or images depicting severe harm to
|
||||
humans.
|
||||
* **Malware and computer viruses**: Software designed to cause harm to computer
|
||||
systems.
|
||||
|
||||
If you have found content which falls in any of these categories on pixeldrain
|
||||
please report the download link to me at
|
||||
[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com) and I will review it. If the
|
||||
content does not fit into one of the categories your e-mail will be ignored.
|
||||
|
||||
Fornaxian Technologies cannot be held liable for any illegal or copyrighted
|
||||
material that's uploaded by the users of this application under the Online
|
||||
Copyright Infringement Liability Limitation Act § 512\(c) in the USA and the
|
||||
Electronic Commerce Directive 2000 Article 14 in the EU.
|
||||
|
||||
## How does pixeldrain store files?
|
||||
|
||||
Pixeldrain uses a few different techniques to store files cheaply, efficiently
|
||||
@@ -87,16 +114,8 @@ When uploading a file pixeldrain will save a list of file links on your
|
||||
browser's local storage. This data is **only** used for viewing your upload
|
||||
history on the [history page](/history).
|
||||
|
||||
## Legality
|
||||
|
||||
I cannot be held liable for any illegal and / or copyrighted material that's
|
||||
uploaded by the users of this application. Files uploaded to this website are
|
||||
subjected to local laws. If laws are being broken, and I've been notified of the
|
||||
fact I'll have to delete the offending content. If you find any files on this
|
||||
domain that break the law, please contact me at
|
||||
[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com), and I'll take care of it.
|
||||
|
||||
Please share responsibly.
|
||||
## Support
|
||||
|
||||
For other questions you can reach me at
|
||||
[support@pixeldrain.com](mailto:support@pixeldrain.com)
|
||||
[support@pixeldrain.com](mailto:support@pixeldrain.com). Abuse reports sent to
|
||||
this address will not be reviewed, use the abuse address.
|
||||
|
@@ -15,12 +15,12 @@
|
||||
* [BurntSushi/toml](https://github.com/BurntSushi/toml)
|
||||
* [julienschmidt/httprouter](https://github.com/julienschmidt/httprouter)
|
||||
* [gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype)
|
||||
* [disintegration/imaging](github.com/disintegration/imaging)
|
||||
* [gorilla/websocket](github.com/gorilla/websocket)
|
||||
* [shopspring/decimal](github.com/shopspring/decimal)
|
||||
* [jhillyerd/enmime](github.com/jhillyerd/enmime)
|
||||
* [disintegration/imaging](https://github.com/disintegration/imaging)
|
||||
* [gorilla/websocket](https://github.com/gorilla/websocket)
|
||||
* [shopspring/decimal](https://github.com/shopspring/decimal)
|
||||
* [jhillyerd/enmime](https://github.com/jhillyerd/enmime)
|
||||
* [russross/blackfriday](https://github.com/russross/blackfriday)
|
||||
* [microcosm-cc/bluemonday](github.com/microcosm-cc/bluemonday)
|
||||
* [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday)
|
||||
|
||||
### Web framework
|
||||
|
||||
|
@@ -22,7 +22,7 @@
|
||||
border-top: 1px solid var(--layer_2_color_border);
|
||||
border-bottom: 1px solid var(--layer_2_color_border);
|
||||
box-sizing: border-box;
|
||||
margin: 10px 0;
|
||||
margin: 1.5em 0;
|
||||
padding: 5px;
|
||||
}
|
||||
.big_number {
|
||||
@@ -122,9 +122,8 @@
|
||||
Upload <u>T</u>ext</button>
|
||||
<br/>
|
||||
<p>
|
||||
By uploading files to pixeldrain you accept that a cookie will
|
||||
be placed in your web browser. More information on the
|
||||
<a href="/about">about</a> page
|
||||
By uploading files to pixeldrain you acknowledge and accept our
|
||||
<a href="/about#content-policy">content policy</a>.
|
||||
<p>
|
||||
<div id="instruction_2" class="instruction_highlight">
|
||||
<div class="limit_width">
|
||||
|
@@ -182,7 +182,9 @@ const toggle_select = () => {
|
||||
{child.name}
|
||||
</td>
|
||||
<td class="node_size">
|
||||
{formatDataVolume(child.file_size, 3)}
|
||||
{#if child.type === "file"}
|
||||
{formatDataVolume(child.file_size, 3)}
|
||||
{/if}
|
||||
</td>
|
||||
</a>
|
||||
{/each}
|
||||
|
88
svelte/src/user_buckets/Bucket.svelte
Normal file
88
svelte/src/user_buckets/Bucket.svelte
Normal file
@@ -0,0 +1,88 @@
|
||||
<script>
|
||||
|
||||
export let bucket
|
||||
let details_hidden = true
|
||||
const expand_bucket = () => {
|
||||
details_hidden = !details_hidden
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="bucket">
|
||||
<div class="bucket_header">
|
||||
<a href={'/d/' + bucket.id} class="bucket_title">
|
||||
<img class="bucket_icon" src="/res/img/mime/folder-remote.png" alt="Bucket icon"/>
|
||||
{bucket.name}
|
||||
</a>
|
||||
<button class="bucket_expand" on:click={expand_bucket}>
|
||||
{#if details_hidden}
|
||||
<i class="icon">expand_more</i>
|
||||
{:else}
|
||||
<i class="icon">expand_less</i>
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<div class="bucket_details" class:hidden={details_hidden}>
|
||||
<form>
|
||||
<table class="form">
|
||||
<tr class="form">
|
||||
<td>Name</td>
|
||||
<td><input type="text" value={bucket.name} /></td>
|
||||
</tr>
|
||||
<tr class="form">
|
||||
<td colspan="2">
|
||||
<button class="button_red">
|
||||
<i class="icon">delete</i> Delete
|
||||
</button>
|
||||
<button class="button_highlight" style="float: right;">
|
||||
<i class="icon">save</i> Save
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.bucket {
|
||||
text-decoration: none;
|
||||
background-color: var(--layer_3_color);
|
||||
transition: box-shadow 0.5s;
|
||||
box-shadow: 1px 1px var(--layer_3_shadow) 0 var(--shadow_color);
|
||||
}
|
||||
.bucket_header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: var(--text_color);
|
||||
}
|
||||
.bucket_header:hover {
|
||||
background-color: var(--input_color_dark)
|
||||
}
|
||||
.bucket_title {
|
||||
flex: 1 1 auto;
|
||||
align-self: center;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
.bucket_icon {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.bucket_expand {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.bucket_details {
|
||||
display: flex;
|
||||
padding: 0.4em;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
border-top: 1px solid var(--layer_3_color_border);
|
||||
color: var(--text_color);
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import Bucket from "./Bucket.svelte";
|
||||
import Spinner from "../util/Spinner.svelte";
|
||||
import { fs_get_buckets } from "../filesystem/FilesystemAPI.svelte";
|
||||
|
||||
@@ -17,10 +18,6 @@ const get_buckets = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const expand_bucket = () => {
|
||||
|
||||
}
|
||||
|
||||
onMount(get_buckets);
|
||||
</script>
|
||||
|
||||
@@ -32,15 +29,23 @@ onMount(get_buckets);
|
||||
{/if}
|
||||
|
||||
<div class="limit_width">
|
||||
<button style="float: right;">
|
||||
<i class="icon">create_new_folder</i> New bucket
|
||||
</button>
|
||||
<br/>
|
||||
<h2>Persistent buckets</h2>
|
||||
<p>
|
||||
These buckets don't expire, but have limited storage space and
|
||||
bandwidth. Their limits can be raised by buying a subscription.
|
||||
</p>
|
||||
{#each buckets as bucket}
|
||||
<a class="bucket_header" href={'/d/' + bucket.id}>
|
||||
<div class="bucket_title">{bucket.name}</div>
|
||||
<button class="bucket_expand" on:click|preventDefault={expand_bucket}><i class="icon">expand_more</i></button>
|
||||
</a>
|
||||
<div class="bucket_details">
|
||||
Hello!
|
||||
</div>
|
||||
<Bucket bucket={bucket}></Bucket>
|
||||
{/each}
|
||||
<br/>
|
||||
<h2>Temporary buckets</h2>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,34 +55,4 @@ onMount(get_buckets);
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
.bucket_header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
text-decoration: none;
|
||||
color: var(--text_color);
|
||||
background-color: var(--layer_3_color);
|
||||
transition: box-shadow 0.5s;
|
||||
box-shadow: 1px 1px var(--layer_2_shadow) 0 var(--shadow_color);
|
||||
}
|
||||
.bucket_header:hover {
|
||||
box-shadow: 0 0 2px 2px var(--highlight_color), inset 0 0 1px 1px var(--highlight_color);
|
||||
color: var(--highlight_color);
|
||||
text-decoration: none;
|
||||
}
|
||||
.bucket_title {
|
||||
flex: 1 1 auto;
|
||||
align-self: center;
|
||||
padding: 0.4em;
|
||||
}
|
||||
.bucket_expand {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.bucket_details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
color: var(--text_color);
|
||||
background-color: var(--layer_3_color);
|
||||
transition: box-shadow 0.5s;
|
||||
}
|
||||
</style>
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"fornaxian.tech/pixeldrain_server/api/restapi/apiclient"
|
||||
"fornaxian.tech/pixeldrain_server/util"
|
||||
"github.com/Fornaxian/log"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
@@ -15,11 +14,9 @@ func (wc *WebController) serveAdClick(w http.ResponseWriter, r *http.Request, p
|
||||
w.Header().Set("Referrer-Policy", "origin")
|
||||
http.Redirect(w, r, r.URL.Query().Get("target"), http.StatusTemporaryRedirect)
|
||||
|
||||
api := apiclient.New(wc.apiURLInternal)
|
||||
|
||||
// The Real IP is used in the API server to determine that the view is not
|
||||
// fake
|
||||
api.RealIP = util.RemoteAddress(r)
|
||||
var api = wc.api.RealIP(util.RemoteAddress(r))
|
||||
|
||||
// Log a view on the file
|
||||
if err := api.PostFileView(p.ByName("id"), wc.viewTokenOrBust()); err != nil {
|
||||
|
@@ -110,7 +110,14 @@ func (wc *WebController) adminAbuseForm(td *TemplateData, r *http.Request) (f Fo
|
||||
Label: "Type",
|
||||
DefaultValue: "unknown",
|
||||
Type: FieldTypeRadio,
|
||||
RadioValues: []string{"unknown", "copyright", "terrorism", "child_abuse"},
|
||||
RadioValues: []string{
|
||||
"unknown",
|
||||
"copyright",
|
||||
"child_abuse",
|
||||
"terrorism",
|
||||
"gore",
|
||||
"malware",
|
||||
},
|
||||
}, {
|
||||
Name: "reporter",
|
||||
Label: "Reporter",
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"fornaxian.tech/pixeldrain_server/api/restapi/apiclient"
|
||||
"fornaxian.tech/pixeldrain_server/util"
|
||||
"github.com/Fornaxian/log"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
@@ -21,9 +20,8 @@ func (wc *WebController) serveFilePreview(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
api := apiclient.New(wc.apiURLInternal)
|
||||
api.APIKey, _ = wc.getAPIKey(r)
|
||||
api.RealIP = util.RemoteAddress(r)
|
||||
apiKey, _ := wc.getAPIKey(r)
|
||||
api := wc.api.Login(apiKey).RealIP(util.RemoteAddress(r))
|
||||
|
||||
file, err := api.GetFileInfo(p.ByName("id")) // TODO: Error handling
|
||||
if err != nil {
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
func (wc *WebController) viewTokenOrBust() (t string) {
|
||||
var err error
|
||||
if t, err = wc.systemPixelAPI.GetMiscViewToken(); err != nil {
|
||||
if t, err = wc.api.GetMiscViewToken(); err != nil {
|
||||
log.Error("Could not get viewtoken: %s", err)
|
||||
}
|
||||
return t
|
||||
@@ -47,9 +47,7 @@ func adType() int {
|
||||
switch i := rand.Intn(5); i {
|
||||
case 0:
|
||||
return amarulaSolutions
|
||||
case 1:
|
||||
return adMaven
|
||||
case 2, 3, 4:
|
||||
case 1, 2, 3, 4:
|
||||
return propellerAds
|
||||
default:
|
||||
panic(fmt.Errorf(
|
||||
|
@@ -29,7 +29,7 @@ type TemplateData struct {
|
||||
Style pixeldrainStyleSheet
|
||||
UserStyle template.CSS
|
||||
APIEndpoint template.URL
|
||||
PixelAPI *apiclient.PixelAPI
|
||||
PixelAPI apiclient.PixelAPI
|
||||
Hostname template.HTML
|
||||
|
||||
// Only used on file viewer page
|
||||
@@ -50,19 +50,19 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request)
|
||||
Style: userStyle(r),
|
||||
UserStyle: template.CSS(userStyle(r).String()),
|
||||
APIEndpoint: template.URL(wc.apiURLExternal),
|
||||
PixelAPI: apiclient.New(wc.apiURLInternal),
|
||||
Hostname: template.HTML(wc.hostname),
|
||||
URLQuery: r.URL.Query(),
|
||||
}
|
||||
|
||||
// Use the user's IP address for making requests
|
||||
t.PixelAPI.RealIP = util.RemoteAddress(r)
|
||||
// Use the user's IP address for making requests
|
||||
PixelAPI: wc.api.RealIP(util.RemoteAddress(r)),
|
||||
|
||||
Hostname: template.HTML(wc.hostname),
|
||||
URLQuery: r.URL.Query(),
|
||||
}
|
||||
|
||||
// If the user is authenticated we'll indentify him and put the user info
|
||||
// into the templatedata. This is used for putting the username in the menu
|
||||
// and stuff like that
|
||||
if key, err := wc.getAPIKey(r); err == nil {
|
||||
t.PixelAPI.APIKey = key // Use the user's API key for all requests
|
||||
t.PixelAPI = t.PixelAPI.Login(key) // Use the user's API key for all requests
|
||||
t.User, err = t.PixelAPI.UserInfo()
|
||||
if err != nil {
|
||||
// This session key doesn't work, or the backend is down, user
|
||||
@@ -71,7 +71,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
if err.Error() == "authentication_required" || err.Error() == "authentication_failed" {
|
||||
// Disable API authentication
|
||||
t.PixelAPI.APIKey = ""
|
||||
t.PixelAPI = wc.api
|
||||
|
||||
// Remove the authentication cookie
|
||||
log.Debug("Deleting invalid API key")
|
||||
|
@@ -5,7 +5,6 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"fornaxian.tech/pixeldrain_server/api/restapi/apiclient"
|
||||
"github.com/Fornaxian/log"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
@@ -16,8 +15,7 @@ func (wc *WebController) serveLogout(
|
||||
p httprouter.Params,
|
||||
) {
|
||||
if key, err := wc.getAPIKey(r); err == nil {
|
||||
var api = apiclient.New(wc.apiURLInternal)
|
||||
api.APIKey = key
|
||||
var api = wc.api.Login(key)
|
||||
if err = api.UserSessionDestroy(key); err != nil {
|
||||
log.Warn("logout failed for session '%s': %s", key, err)
|
||||
}
|
||||
@@ -150,7 +148,7 @@ func (wc *WebController) loginForm(td *TemplateData, r *http.Request) (f Form) {
|
||||
}
|
||||
|
||||
if f.ReadInput(r) {
|
||||
loginResp, err := td.PixelAPI.UserLogin(f.FieldVal("username"), f.FieldVal("password"), false)
|
||||
loginResp, err := td.PixelAPI.UserLogin(f.FieldVal("username"), f.FieldVal("password"))
|
||||
if err != nil {
|
||||
formAPIError(err, &f)
|
||||
} else {
|
||||
|
@@ -170,8 +170,7 @@ func (wc *WebController) serveEmailConfirm(
|
||||
var err error
|
||||
var status string
|
||||
|
||||
api := apiclient.New(wc.apiURLInternal)
|
||||
err = api.UserEmailResetConfirm(r.FormValue("key"))
|
||||
err = wc.api.UserEmailResetConfirm(r.FormValue("key"))
|
||||
if err != nil && err.Error() == "not_found" {
|
||||
status = "not_found"
|
||||
} else if err != nil {
|
||||
|
@@ -36,9 +36,10 @@ type WebController struct {
|
||||
|
||||
httpClient *http.Client
|
||||
|
||||
// This API client should only be used for system functions like getting
|
||||
// view tokens. It has no authentication and no IP forwarding
|
||||
systemPixelAPI *apiclient.PixelAPI
|
||||
// API client to use for all requests. If the user is authenticated you
|
||||
// should call Login() on this object. Calling Login will create a copy and
|
||||
// not alter the original PixelAPI, but it will use the same HTTP Transport
|
||||
api apiclient.PixelAPI
|
||||
}
|
||||
|
||||
// New initializes a new WebController by registering all the request handlers
|
||||
@@ -60,7 +61,7 @@ func New(
|
||||
apiURLExternal: apiURLExternal,
|
||||
sessionCookieDomain: sessionCookieDomain,
|
||||
httpClient: &http.Client{Timeout: time.Minute * 10},
|
||||
systemPixelAPI: apiclient.New(apiURLInternal),
|
||||
api: apiclient.New(apiURLInternal),
|
||||
}
|
||||
wc.templates = NewTemplateManager(resourceDir, apiURLExternal, debugMode)
|
||||
wc.templates.ParseTemplates(false)
|
||||
@@ -218,7 +219,7 @@ func (wc *WebController) serveMarkdown(tpl string, requireAuth bool) httprouter.
|
||||
var inHeader = false
|
||||
blackfriday.New(
|
||||
blackfriday.WithRenderer(renderer),
|
||||
blackfriday.WithExtensions(blackfriday.CommonExtensions),
|
||||
blackfriday.WithExtensions(blackfriday.CommonExtensions|blackfriday.AutoHeadingIDs),
|
||||
).Parse(
|
||||
tplBuf.Bytes(),
|
||||
).Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
@@ -337,8 +338,7 @@ func (wc *WebController) getAPIKey(r *http.Request) (key string, err error) {
|
||||
func (wc *WebController) captchaKey() string {
|
||||
// This only runs on the first request
|
||||
if wc.captchaSiteKey == "" {
|
||||
var api = apiclient.New(wc.apiURLInternal)
|
||||
capt, err := api.GetRecaptcha()
|
||||
capt, err := wc.api.GetRecaptcha()
|
||||
if err != nil {
|
||||
log.Error("Error getting recaptcha key: %s", err)
|
||||
return ""
|
||||
|
Reference in New Issue
Block a user