Fully rename Pixeldrain to Nova

This commit is contained in:
2026-06-10 23:53:03 +02:00
parent 3c13cd1a14
commit 7b24a9d8cf
95 changed files with 321 additions and 1989 deletions

View File

@@ -1,23 +1,23 @@
# pixeldrain_web
# nova_web
Web interface for pixeldrain.com
Web interface for Nova.storage
## Running
Clone the repo:
```
git clone git@github.com:Fornaxian/pixeldrain_web.git
git clone git@scm.fornaxian.tech:pixeldrain/fnx_web.git
```
Enter the directory and run main.go with `go run main.go`. It will generate a
configuration file for you. The default configuration serves the web UI on
http://127.0.0.1:8081. It contains a reverse proxy server which sends all API
requests to the production endpoint at https://pixeldrain.com/api. You can log
in with your real pixeldrain account on your development server by going to
requests to the production endpoint at https://nova.storage/api. You can log
in with your real Nova account on your development server by going to
http://127.0.0.1:8081/login.
All except for one of pixeldrain's API endpoints are publicly available. Because
All except for one of Nova's API endpoints are publicly available. Because
of this you can do everything with the locally hosted instance which you can do
with the real site. The one thing which is missing is the view registration on
the file viewer. Views are verified on the server side, this does not work when

View File

@@ -1,628 +0,0 @@
# Fornax's Guide To Ridiculously Fast Ethernet
- [Introduction](#introduction)
- [Sysctls](#sysctls)
- [net.ipv4.tcp_congestion_control](#net-ipv4-tcp-congestion-control)
- [net.core.default_qdisc and txqueuelen](#net-core-default-qdisc-and-txqueuelen)
- [net.ipv4.tcp_shrink_window](#net-ipv4-tcp-shrink-window)
- [net.ipv4.tcp_{w,r}mem](#net-ipv4-tcp-w-r-mem)
- [net.ipv4.tcp_mem](#net-ipv4-tcp-mem)
- [Network Interface Cards](#network-interface-cards)
- [ethtool](#ethtool)
- [Channels (ethtool -l)](#channels-ethtool-l)
- [Ring buffers (ethtool -g)](#ring-buffers-ethtool-g)
- [Interrupt Coalescing (ethtool -c)](#interrupt-coalescing-ethtool-c)
- [BIOS](#bios)
- [NUMA Nodes per socket](#numa-nodes-per-socket)
- [SMT Control](#smt-control)
- [IOMMU](#iommu)
- [Reverse proxy](#reverse-proxy)
- [HTTP/2 or QUIC?](#http-2-or-quic)
- [Operating system](#operating-system)
- [Kernel](#kernel)
- [That's all, folks!](#that-s-all-folks)
## Introduction
If you are one of the lucky few who has a fast enough connection, you might have
just downloaded a 5 GB file in 10 seconds and wondered how that is even
possible. Well, it took a lot of effort to get there.
When I first ordered a 100 GbE server I expected things to just work. Imagine my
surprise when the server crashed when serving at just 20 Gigabit.
Then I expected my server host to be able to help with the performance problems.
Spoiler alert: They could not help me.
That's where my journey into the rabbit hole of network performance started. I
paid good money for that 100 Gigabit connection and I'll be damned if I can't
use all of it. I'm getting to the bottom of this no matter how long it takes...
It took me well over a year to figure out all the details of high speed
networking. My good friend [Jeff
Brandt](https://www.linkedin.com/in/jeff-brandt-51b2a65/) (who has been hosting
pixeldrain for nearly ten years now) was able to point me in the right direction
by explaining the basics and showing me some sysctls and ethtool commands which
might affect performance. That was just the entrance of the rabbit hole though,
and this one carried on deep. After about a year of trial and error pixeldrain
can finally serve files at 100 Gigabit per second.
Below is a summary of everything I discovered during my year of reading NIC
manuals, digging through the kernel sources, running profilers, patching the
kernel, learning about CPU topology and TCP inner workings.
## Sysctls
When looking into network performance problems the `sysctl`s are usually the
first thing you get pointed at. There is **a ton** of conflicting information
online about which sysctls do what and what to set them to.
Sysctls are not persistent through reboots, add these lines to
`/etc/sysctl.conf` to apply them at startup.
Through experimentation and kernel recompilation I finally settled on these
values:
### net.ipv4.tcp_congestion_control
You might have heard of BBR. Google's new revolutionary congestion control
algorithm. You might have heard conflicting information about how good it is. I
have extensively tested all congestion controls in the kernel and I can say
without a doubt that BBR is the best, by far! BBR is the only algo which does
not absolutely tank your transfer rate when a packet is lost.
TCP BBR was merged into the kernel at version 4.9. I know the sysctl says ipv4,
but it works for IPv6 as well.
`net.ipv4.tcp_congestion_control=bbr`
### net.core.default_qdisc and txqueuelen
The qdisc (queuing discipline) is another param which gets mentioned often. The
qdisc orders packets which are queued so they can be sent in the most efficient
order possible. The thing is, when you're sending at 100 Gbps then queuing is
completely irrelevant, the network is rarely the bottleneck here.
Google used to require `fq` with `bbr`, but that requirement has been dropped. I
suggest you use something minimal and fast. How about `pfifo_fast`, it has fast
in the name, must be good, right? This is actually already the default on Linux
nowadays, so there's not really a need to change it.
`net.core.default_qdisc=pfifo_fast`
A queue must have a size though. Linux gives the network queues a size of 1000
packets by default. As we'll learn later, a thousand packets is really not a lot
when running at 100 Gbps. When the queue is full the kernel will actually drop
packets, which is absolutely not what we want. So we increase the queue length
to 10000 packets instead:
`ip link set $INTERFACE txqueuelen 10000`
### net.ipv4.tcp_shrink_window
This sysctl was developed by Cloudflare. The patch was merged into Linux 6.1. If
you are on an older kernel version than 6.1 you will need to manually apply [the
patches](https://github.com/cloudflare/linux/) and compile the kernel on your
machine. Without this patch the kernel will waste so much time and memory on
buffer management that by the time you reach 100 Gigabit the kernel wont even
have time to run your app anymore.
Cloudflare has an extensive writeup about the problem this sysctl solves here:
[Unbounded memory usage by TCP for receive buffers, and how we fixed
it](https://blog.cloudflare.com/unbounded-memory-usage-by-tcp-for-receive-buffers-and-how-we-fixed-it/)
This sysctl makes sure that TCP buffers are shrunk if they are larger than they
need to be. Without this sysctl your buffers will just continue to grow until
memory runs out! Before I discovered this patch my servers would regularly run
out of memory during peak load, and these are servers with a **TeraByte of
RAM**! After applying the patches (and compiling the kernel, because the patches
were not merged yet back then) memory usage from TCP buffers was reduced by 80%
on my systems. And performance has improved considerably. This patch is so
crucial for performance that it boggles my mind that it's not enabled by
default. It's even described in the [TCP
spec](https://www.rfc-editor.org/rfc/rfc7323#section-2.4), it's standardized
behaviour. If you're a kernel or systemd developer, please consider just turning
this on by default instead of hiding it behind a toggle.
`net.ipv4.tcp_shrink_window=1`
Cloudflare has some other sysctls as well, but those focus more on latency than
throughput. You can find them here: [Optimizing TCP for high WAN throughput
while preserving low
latency](https://blog.cloudflare.com/optimizing-tcp-for-high-throughput-and-low-latency/).
The `net.ipv4.tcp_collapse_max_bytes` sysctl they write about here was never
merged into the kernel. But while it does improve latency a bit, it's not that
important for throughput.
### net.ipv4.tcp_{w,r}mem
These variables dictate how much memory can be allocated for your send and
receive buffers. The send and receive buffers are where TCP packets are stored
which are not yet acknowledged by the peer. The required size of these buffers
depends on your [Bandwidth-Delay Product
(BDP)](https://en.wikipedia.org/wiki/Bandwidth-delay_product). This concept is
crucial to understand. If you set the TCP buffers too small it will literally
put a speed limit on your connection.
First let's go over how TCP sends data. TCP can retransmit packets if the client
did not receive them. To do this TCP needs to keep all the data it sends to the
client in memory until the client acknowledges (ACK) that it has been properly
received. The acknowledgment takes one round trip to the client and back.
Let's say you want to send a file from Amsterdam to Tokyo. The server sends the
first packet, 130ms later the client in Tokyo receives the data packet. The
client then sends ACK to tell the server that the packet was properly received,
the ACK takes 130ms to arrive back in Amsterdam. Only now can the server remove
the packet from memory. The whole exchange took 260ms.
Now let's say we want to send files at 10 Gigabit. 10 Gigabit is 1250 MB. We
multiply the number of bytes we want to send per second by the number of seconds
it takes to get back the ACK. That's `1250 MB * 0.260 s = 325 MB`. Now we know
that our buffer needs to be at least 325 MB to reach a speed of 10 Gigabit over
a 260ms round trip.
The kernel also stores some other TCP-related stuff in that memory, and we also
need to account for packet loss which causes packets to be stored for a longer
time. I also don't want the speed to be limited to 10 Gbps, we're running a 100
GbE NIC after all. For this reason pixeldrain servers use a maximum buffer size
of 1 GiB.
```
net.ipv4.tcp_wmem='4096 65536 1073741824'
net.core.wmem_max=1073741824
net.ipv4.tcp_rmem='4096 65536 1073741824'
net.core.rmem_max=1073741824
```
The three values in the wmem and rmem are the minimum buffer size, the default
buffer size and the maximum buffer size. The pixeldrain server application uses
64k reusable buffers (with [sync.Pool](https://pkg.go.dev/sync#Pool)) all over
the codebase. For this reason we initialize the window size at 64k as well.
### net.ipv4.tcp_mem
We just configured the buffer sizes, what's this for then? Well... we can tune
TCP buffers per connection all we want, but all that is for nothing if the
kernel still limits the TCP buffers globally.
This sysctl configures how much system memory can be used for TCP buffers. On
boot these values are set based on available system memory, which is good. But
by default it only uses like 5% of the memory, which is not even close to
enough. We need to pump those numbers way up to get anywhere near the speed that
we want.
tcp_mem is defined as three separate values. These values are in numbers of
memory pages. A memory page is usually 4096B. Here is what these three values mean:
* `low`: When TCP memory is below this threshold then TCP buffer sizes are not
limited.
* `pressure`: When the TCP memory usage exceeds this threshold it will try to
shrink some TCP buffers to free up memory. It will keep doing this until
memory usage drops below `low` again. Shrinking TCP buffers takes a lot of
CPU time, and during this time no data is sent to the client. You don't want
to set `low` and `pressure` too far apart.
* `high`: The TCP system can't allocate more than this number of pages. If this
limit is reached and a new TCP session is opened it will not be able to
allocate any memory. Needless to say this is terrible for performance.
After a lot of experimentation with these values I have come to the conclusion
that the best values for these parameters are 40% of RAM, 50% of RAM and 60% of
RAM. This will use most of the RAM for TCP buffers if needed, but also leaves
plenty for your applications.
I set these values dynamically per host with Ansible:
```yaml
{{noescape `- name: configure tcp_mem
sysctl:
name: net.ipv4.tcp_mem
value: "{{ (mempages|int * 0.4)|int }} {{ (mempages|int * 0.5)|int }} {{ (mempages|int * 0.6)|int }}"
state: present
vars:
mempages: "{{ ansible_memtotal_mb * 256 }}" # There are 256 mempages in a MiB`}}
```
## Network Interface Cards
There are lots of NICs to choose from. From my testing every NIC seems to behave
differently. The only NIC types I have had any luck with are ConnectX-5 and
ConnectX-6. Intel's E810 NICs are also not terrible, but Nvidia cards seem to
fare much better with high connection counts. I currently have two servers with
E810 cards and two servers with ConnectX-6 cards. The E810 cards are usually the
first to crap out during a load peak. NICs are just fickle beasts overall. I
don't know if my experiences are actually related to the quality of the cards,
or just bad luck with faulty hardware.
Often you see advice to install a proprietary driver for your NIC. Don't do
that. In my experience that has only caused problems. Nvidia's NIC drivers are
just as shitty as their video drivers. They will break kernel updates and
generally make your life miserable. The drivers in the Linux kernel are good and
well maintained. You don't need to taint your kernel with some scary proprietary
blob.
Upgrading the firmware for your NIC can be a good idea, if you can figure out
how, that is. Nvidia's tools for upgrading firmware are a huge hassle to work
with and the documentation is outdated and scarce.
## ethtool
Ethtool is a program which you can use to configure your network card. There is
lots of stuff to configure here, but there are only three settings which really
matter.
Ethtool needs your network interface name for every operation. In this guide we
will refer to your interface name as `$INTERFACE`. You can get your interface
name from `ip a`.
Ethtool options are not persistent through reboots. And there's no configuration
file to put them in either. So you'll need to put them in a script which runs
somewhere in the boot process somehow.
### Channels (ethtool -l)
The channels param configures how many CPU cores will communicate with the NIC.
You generally want this number to be equal to the number of CPU cores you have,
that way the load will be evenly spread across your CPU. If you have more CPU
cores than your NIC supports you can try turning multithreading off in the BIOS.
Or just accept that only a portion of your cores will communicate with the NIC,
it's not that big of a problem.
If you are running on a multi-CPU platform you only want one CPU to communicate
with the NIC. Distributing your channels over multiple CPUs will cause cache
thrashing which absolutely tanks performance. Many of pixeldrain's server are
dual CPU, where one CPU runs the pixeldrain software and the other only
communicates with the NIC. Buying a $10k CPU just to talk to a NIC is a bit
wasteful, I recommend just using one CPU if you have the choice.
Your NIC will usually configure the channels correctly on boot, so in most of
the cases you don't need to change anything here. You can query the settings
with `ethtool -l $INTERFACE` and update the values like this: `ethtool -L
$INTERFACE combined 63`.
### Ring buffers (ethtool -g)
The ring buffers are portions of RAM where the NIC stores your IP packets before
they are sent out to the network (tx) or sent to the CPU (rx). Increasing the
ring buffer sizes can increase network latency a little bit because more packets
are getting buffered before being sent out to the network. But again, at 100 GbE
this happens so fast that the difference is in the order of microseconds, that
makes absolutely no difference to us. We just want to move as much data as
possible in as little time as possible.
If we can buffer more packets then it means we can transfer more data in bulk
with every clock cycle. So we simply set this to the maximum. For Mellanox cards
the maximum is usually `8192`, but this can vary. Check the maximum values for
your card with `ethtool -g $INTERFACE`.
Set the receive and send buffers to 8192 packets: `ethtool -G $INTERFACE rx 8192
tx 8192`
### Interrupt Coalescing (ethtool -c)
The NIC can't just write your packets to the CPU and expect it to do something
with them. Your CPU needs to be made aware that there is new data to process.
That happens with an interrupt. Ethtool's interrupt coalescing values tell the
NIC when and how to send interrupts to the CPU. This is a delicate balance. We
don't want to interrupt the CPU too often, because then it won't be able to get
any work done. That's like getting a new ping in team chat every half hour, how
are you supposed to concentrate like that? But if we set the interrupt rate too
slow, the NIC won't be able to send all packets in time before the buffers fill
up.
The interrupt coalescing options vary a lot per NIC type.. These are the ones
which are present on my ConnectX-6 Dx: `rx-usecs`, `rx-frames`, `tx-usecs`,
`tx-frames`, `cqe-mode-rx`, `cqe-mode-tx`. I'll explain what these are:
* `rx-usecs`, `tx-usecs`: These values dictate how often the NIC interrupts the
CPU to receive packets `rx` or send packets `tx`. The value is in
microseconds. The SI prefix for micro is µ, but for convenience they use the
letter u here. A microsecond is one-millionth of a second.
* `rx-frames`, `tx-frames`: Like the values above this defines how often the
CPU is interrupted, but instead of interrupting the CPU at a fixed interval
it interrupts the CPU when a certain number of packets are in the buffer.
* `cqe-mode-rx`, `cqe-mode-tx`: These options enable packet compression in the
PCI bus. This is handy if your PCI bus is a bottleneck, like when your 100G
NIC is plugged into 4x PCI 4.0 lanes, which only has 7.88 GB/s bandwidth. In
most cases it's best to leave these at the default value.
* `adaptive-rx`, `adaptive-tx`: These values tell the NIC to calculate its own
interrupt timings. This disregards the values we configure ourselves. The
timings calculated by the NIC often prefer low latency over throughput and
can quickly overwhelm the CPU with interrupts. So for our purposes this needs
to be disabled.
So what are good values for these? Well, we can do some math here. Our NIC can
send 100 Gigabits per second. That's 12.5 GB. A network packet is usually 1500
bytes. This means that we need to send 8333333 packets per second to reach full
speed. Our ring buffer can hold 8192 packets, so if we divide by that number we
learn that we need to send 1017 entire ring buffers per second to reach full
speed.
Waiting for the ring buffer to be completely full is probably not a good idea,
since then we can't add more packets until the previous packets have been copied
out. So we want to be able to empty the ring buffer twice. That leaves us with
2034 ring buffers per second. Now convert that buffers per second number to µs
per buffer: `1000000 / 2034 = 492µs`, we land on a value of 492µs per interrupt.
This is our ceiling value. Higher than this and the buffers will overflow. But
492µs is nearly half a millisecond, that's an eternity in CPU time. That's high
enough that it might actually make a measurable difference in packet latency. So
we opt for a more sane value of 100µs instead. That still gives the CPU plenty
of time to do other work in between interrupts. A 3 GHz CPU core will be able to
perform about 30000 calculations inbetween each interrupt. At the same time it's
low enough to barely make a measurable difference in latency, at most a tenth of
a millisecond.
As for the `{rx,tx}-frames` variables. We just spent all that time calculating
the ideal interrupt interval, I don't really want the NIC to start interrupting
my CPU when it's not absolutely necessary. So we use the maximum ring buffer
value here: `8192`. Your NIC might not support such high coalescing values. You
can also try setting this to `4096` or `2048` if you notice problems.
That leaves us with this configuration:
```
ethtool -C $INTERFACE adaptive-rx off adaptive-tx off \
rx-usecs 100 tx-usecs 100 \
rx-frames 8192 tx-frames 8192
```
Tip: If you want to see how much time your CPU is spending on handling
interrupts, go into `htop`, then to Setup (F2) and enable "Detailed CPU time"
under Display options. The CPU gauge will now show time spent on handling
interrupts in purple. Press F10 to save changes.
## BIOS
Not even the BIOS is safe from our optimization journey. If fact, some of the
most important optimizations must be configured here.
### NUMA Nodes per socket
Big CPUs with lots of cores often segment their memory into NUMA nodes. These
smaller nodes get exclusive access to a certain portion of RAM and don't have to
contend over memory access with the other NUMA nodes. This can improve your
performance... if your software supports it well. But from my testing the setup
of one NIC queue per core does not combine well with having multiple NUMA nodes.
The fact that I use Go, which does not have a NUMA aware scheduler as far as I
know, probably does not help either. For these reasons I prefer to set `NUMA
nodes per socket` to `NPS1`.
Some AMD BIOSes also have an option called `ACPI SRAT L3 Cache as NUMA Domain`.
This will create NUMA nodes based on the L3 cache topology, *even if you
explicitly disabled NUMA in the memory addressing settings*. To fix this set
`ACPI SRAT L3 Cache as NUMA Domain` to `Disabled`.
### SMT Control
Multithreading (or Hyperthreading, on Intel) can be a performance booster, but
it can also be a performance bottleneck. If you have a CPU with a lot of cores,
like AMD's Epyc lineup, then disabling SMT can be a good way to improve per-core
performance.
Most apps have no way to effectively use hundreds of CPU threads. At some point
adding more threads will only consume more memory and CPU cycles just because
the kernel scheduler, memory controller and your language runtime have to manage
all those threads. This can cause huge amounts of overhead. My rule of thumb: If
you have 64 or more cores: `SMT OFF`
### IOMMU
The [Input-output memory management
unit](https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit)
is a CPU component for virtualizing your memory access. This can be useful if
you run a lot of VMs for example. You know what it's also good for? **Completely
destroying NIC performance**.
A high end NIC needs to shuffle a lot of data over the PCI bus. A 100 GbE NIC in
full duplex can reach up to 25 GB/s! When the IOMMU is enabled it means that all
the data that the NIC sends/receives needs to go through the IOMMU first before
it can go into RAM. This adds a little bit of latency. When you are running a
high end NIC in your PCI slot, then the added latency makes sure that your NIC
will **never ever get anywhwere near the advertised speed**. In some cases the
overhead is so large that the NIC will effectively drop off the PCI bus,
immediately crashing your system once it gets only slightly overloaded. Yes,
really, I have seen this happen.
Seriously, if you have a high end NIC plugged into your PCI slot and you have
the IOMMU enabled. **You might as well plug a goddamn brick into your PCI
slot**, because that's about as useful as your expensive NIC will be.
It took me way too long to find this information. The difference between IOMMU
off and on is night and day. I am actually **furious** that it took me so long
to discover this. I spent *weeks* pulling hair out of my head trying to figure
out why my NIC was locking up whenever I tried to put any real load on it. All
the NIC tuning guides I could find talk about tweaking little ethtool params,
installing drivers, updating firmware and useles crap like that, the IOMMU was
completely omitted in every one of them. I was getting so desperate with my
terrible NIC performance that I just started flipping toggles in the BIOS to see
if anything made a difference. If you have any idea how long it takes to reboot
a high end server system you know how tedious this is. That's how I discovered
that the IOMMU was the source of **all my problems**.
Ugh, just thinking about all the time I wasted because because nobody told me to
just turn the IOMMU off gets my blood boiling. That's why I am writing this
guide, I want to spare you the suffering.
So yea... `AMD CBS > NBIO Common Options > IOMMU > Disabled` ...AND STAY DOWN!
I also just turn off anything related to virtualization nowadays. Having
virtualization options enabled when you are not running VMs is a waste of
resources. No worries, docker is not virtualization, it's just namespacing,
nothing virtual about that. And if you are running VMs.. well, consider bare
metal. It's really not that scary and there is lots of performance to be gained.
You can verify that your IOMMU is disabled with this command `dmesg | grep
iommu`. Your IOMMU is disabled if it prints something along the lines of:
```
[ 1.302786] iommu: Default domain type: Translated
[ 1.302786] iommu: DMA domain TLB invalidation policy: lazy mode
```
If you see more output than that, you need to drop into the BIOS and nuke that
shit immediately.
One little caveat is that Linux requires the IOMMU to support more than 255 CPU
threads. So if you have 256 threads and the IOMMU is turned off one of your
threads will be disabled. So once again I will repeat my rule of thumb with
regards to multithreading: If you have 64 or more cores: `SMT OFF`
## Reverse proxy
A lot of sites run behind a reverse proxy like nginx or Caddy. It seems to be an
industry standard nowadays. People are surprised when they learn that pixeldrain
does not use one of the standard web servers.
As it turns out, 100 Gigabit per second is a lot of data. It takes a
considerable amount of CPU time to churn through that much data, so ideally you
want to touch it as few times as you can. At this scale playing hot potato with
your HTTP requests is a really bad idea.
A big bottleneck with networking on Linux is copying data across the kernel
boundary. The kernel always needs to copy your buffers because userspace is
dirty, ew, would not want to share memory with that. When you are running a
reverse proxy every HTTP request is effectively crossing the kernel boundary
*six times*. Let's assume we're running nginx here, the client sends a request
to the server. The kernel copies the request body from kernel space to nginx's
listener (from kernel space to userspace), nginx opens a request to your app and
copies the body the to localhost TCP socket (back to kernel space). The kernel
sends the body to your app's listener on localhost (now it's in userspace
again). And then the response body follows the same path again. Request: NIC ->
kernel -> userspace -> kernel -> userspace. Response: userspace -> kernel ->
userspace -> kernel -> NIC. That's crazy inefficient.
That's why pixeldrain just uses Go's built in HTTP server. Go's HTTP server is
very complete. Everything you need is there:
* [Routing](https://github.com/julienschmidt/httprouter)
* [TLS (for HTTPS)](https://pkg.go.dev/crypto/tls)
* HTTP/2
* Even a [reverse
proxy](https://pkg.go.dev/net/http/httputil#NewSingleHostReverseProxy) if
you're into that kinda stuff
The only requirement is that your app is written in Go. Of course other
languages also have libraries for this.
Zero-downtime restarts are a bit tricky. Luckily the geniuses tinkering away at
the Linux kernel every day made something neat for us. It's called
`SO_REUSEPORT` (Wow! Catchy name!). By putting this socket option on your TCP
listener you allow future instances of your server process to listen on the same
port at the same time. By doing this your upgrades become really quite simple:
1. Upload new server executable to the server.
2. Start the new executable up.
3. When everything is initialized it starts listening on the same port as the
previous process using `SO_REUSEPORT`.
4. After the listener is installed we signal to the old server process (which is
still running at this point) that it can start shutting down. The listener is
closed and the active HTTP requests are gracefully completed.
5. Once the old listener is closed all new requests will go to the new process
and the upgrade is complete.
Now there may be one question on your mind: How do I signal to the previous
process that the new process has finished initializing? I have just the thing
for you. [This handy-dandy library that I
made](https://github.com/Fornaxian/zerodown). I use it for pixeldrain and it
works like a charm. Your software updates are just one `SIGHUP` away from being
deployed.
## HTTP/2 or QUIC?
HTTP/2 and QUIC (HTTP/3) are new revisions of the HyperText Transfer Protocol.
HTTP/2 introduces multiplexing which significantly reduces handshake latency.
HTTP/1.1 will open a separate TCP session for each file it needs to request,
HTTP/2 opens one connection instead and uses framing to send multiple requests
at the same time instead, this allows the connection to ramp up to a higher
speed and quicker. This goes hand in hand with the BBR congestion control
algorithm which also significantly reduces connection ramp-up time. The result
is 60% faster loading times for web pages on average.
HTTP/2 is trivially enabled in the Go HTTP server. Simply add `NextProtos =
[]string{"h2"}` to your `tls.Config` and it's good to go. An annoying
implementation detail is that Go's HTTP/2 server throws completely different
errors than HTTP/1.1, so you will have to redo all your error handling. To make
matters worse, HTTP/2's errors are not exported by the `http` package, so you
have to resort to string searching to catch these errors.. 😒.
Then along comes HTTP/3, also known as QUIC. HTTP/3 throws everything we just
did out of the window and uses UDP instead. It moves all the buffer management
and congestion control to userspace. Sure, you get more control that way, but
that's really only useful if you're Google. I tried the most popular HTTP/3
server implementation for Go, and it struggled to even reach half of the
throughput I got with HTTP/2. Sure, latency is lower, but that's not that useful
to me when the most important part of my site stops functioning. Sure, TCP is
not perfect, but it's better than having to do everything yourself.
To summarize, if you only care about throughput: HTTP/2 👍 HTTP/3 👎 (for now)
## Operating system
Choose something up-to-date, lightweight and minimalist. Pixeldrain used to run
on Ubuntu because I was familiar with it, but over time Ubuntu server got more
bloated and heavy. Unnecessary stuff was being added with each new release
(looking at you snapd), and I just didn't want to deal with that. Eventually I
switched to Debian.
Debian is so much better than Ubuntu. After booting it for the first time there
will only be like 10 processes running on the system, just the essentials. It
really is a clean sandbox waiting for you to build a castle in it. It might take
some getting used to, but it will definitely pay off.
Anyway, that's just my opinion. In reality you can pick any distro you like. It
does not really matter that much. Just keep in mind that some distro's ship
newer kernels than others, and that's really quite important as we will learn in
the next paragraph.
## Kernel
You need to run at least kernel 6.1, because of the `net.ipv4.tcp_shrink_window`
sysctl. But generally, **newer is better**. There are dozens of engineers from
Google, Cloudflare and Meta tinkering away at the Linux network stack every day.
It gets better with every release, really, the pace is staggering.
But doesn't Debian ship quite old kernel packages? (you might ask) Yes... kinda.
By using [this guide](https://wiki.debian.org/HowToUpgradeKernel) you can
upgrade your kernel version to the `testing` or even the `experimental` branch
while keeping the rest of the OS the same.
On the [Debian package tracker](https://tracker.debian.org/pkg/linux) you can
see which kernel version ships in which repository. This is useful for picking
which repo you want to use for your kernel updates. Pixeldrain gets its kernel
updates from the `testing` branch. These are kernels which have been declared
stable by the kernel developers and are generally safe to use.
Keep an eye on the [Phoronix Linux Networking
blog](https://www.phoronix.com/linux/Linux+Networking) for new kernel features.
Pretty much every kernel version that comes out boasts about huge network
performance wins. I'm personally waiting for Kernel 6.8 to come out. They are
promising a 40% TCP performance boost. Crazy!
## That's all, folks!
**Behold.. One hundred gigabits per second!**
![nload showing 85 Gbps](/res/img/100gbps.webp)
Actually my nload seems to cap out at around 87 Gbps.. there's probably some
overhead somewhere. It's close though.
I hope this guide was useful to you. I wish I had something like this when I
started out. I could have quite literally saved me months of time. Then again,
chasing 100 Gigabit is one of the most educative challenges I have ever faced. I
have learned so much about Linux's structure, kernel performance profiling, CPU
architecture, the PCIe bus and tons of other things that I would never have
known if I did not go down this rabbit hole. And I have a feeling the journey is
not over. I will always have this urge to get the absolute most out of my
servers. I'm paying for the whole CPU and I'm going to use the whole CPU after
all.
Anyway, check out [Pixeldrain](/) if you like, it's the fastest way to transfer
files across the web. And I'm working on a [cloud storage](/filesystem) offering
as well. It has built in rclone and FTPS support. Pixeldrain also has a built in
[speedtest](/speedtest) which you can use to see the fruits of my labour. The
source for this document is available in markdown format on [my
GitHub](https://github.com/Fornaxian/pixeldrain_web/blob/master/res/include/md/100_gigabit_ethernet.md).
Follow me on [Mastodon](https://mastodon.social/@fornax),
[Twitter](https://twitter.com/Fornax96), join our
[Discord](https://discord.gg/TWKGvYAFvX), et cetera et cetera

View File

@@ -1,260 +0,0 @@
# Questions and Answers
[TOC]
## For how long will my files be stored?
Files will be removed if they have not been accessed for 120 days. When a file
is downloaded the expiry time is reset to 120 days from the current day. This
only happens when someone downloads more than 10% of the whole file in a single
request. So if you have a 5 GB file the timer is only extended when you download
at least 500 MB. The expiry timer is not updated when it was already updated
within the last 24 hours.
File expiry is often seen as a downside of pixeldrain. But keep in mind that 120
days is a very long time. Roughly four months. This means that you can keep a
file active for an entire year by only downloading it three times. Files which
are only very sporadically downloaded can stay online indefinitely. All this
time the file is using storage space and processing power on our servers, which
costs real money.
If you would like to use pixeldrain for backups or long term storage, then the
free plan is _not the way to go_. It is only meant for publicly sharing files.
For real storage you should use the [pixeldrain filesystem](/filesystem).
If you are (ab)using pixeldrain for free storage, you should not be surprised
when the rules change at some point and all your stuff suddenly disappears. That
would never happen when using pixeldrain's filesystem.
## What cookies does pixeldrain use?
When logging in to a pixeldrain account a cookie named 'pd_auth_key' will be
installed. This cookie keeps your login session active. When you delete it you
will be logged out of your account.
When you use the style selector on the [Appearance](/appearance) page a cookie
called 'style' will be set. This cookie controls the appearance of the website
for you.
Pixeldrain does not use tracking cookies. We also don't use fingerprinting to
track our users. The only information that is saved is the information that you
manually enter or upload.
## How does the transfer limit work?
Pixeldrain has two kinds of transfer limit, the free limit for users without a
subscription and the premium limit for users with a subscription.
### Free
The free limit tracks how much you have downloaded from pixeldrain in the last
24 hours from now. The limit is _not per day_, instead it just keeps track of
when you downloaded something and if it was less than 24 hours ago it counts
towards your limit. In technical terms this is called a 'Sliding window
algorithm'.
The free download limit is only tracked per _IP address_. This means that if you
are sharing an IP address with other people, like through a VPN, company network
or a CGNAT network then the download limit is also shared. For free downloads it
makes no difference if you're logged in to an account or not.
When the limit is exceeded you can still download, but file previews are
disabled and download speed is reduced.
### Premium
The premium transfer limit works similarly to the free download limit. The
differences are that the limit is bound to an account instead of an IP address,
and the limit is per 30 days instead of 24 hours. The same sliding window system
still applies. Any data that was transferred to/from your account between now
and 30 days ago counts towards your limit.
Whenever someone downloads a file from your account it counts toward your
transfer limit. If you want to limit how much of your transfer cap others can
use then you can configure a limit on the [sharing settings
page](/user/sharing).
If the person who downloads the file also has a premium account then their own
data cap will be used first.
## How does hotlinking work?
Hotlinking happens when someone downloads a file from pixeldrain without
visiting the pixeldrain website. This can be through embedding media files on
third party websites, or using download managers to download files directly.
Pixeldrain has a "hotlink protection mode". This activates when we detect that a
file is being hotlinked while neither the downloader nor the uploader of the
file has a premium subscription. When this happens a CAPTCHA test will appear on
the file's download page, and the file can only be downloaded once the CAPTCHA
is solved. When enough people complete the test the hotlink protection will be
removed and the file can be downloaded normally again.
There are two reasons why we implemented hotlink protection:
File hosting services are often used to spread malware and other nefarious data,
hotlink protection makes it significantly harder for people to abuse the service
in this way. This was the original motivation for implementing hotlink
protection, it has been very effective at preventing digital attacks.
Hotlinking also uses pixeldrain's bandwidth and processing power without letting
the user know that they are using pixeldrain. People who don't know that they
are using pixeldrain are less likely to purchase a premium plan. The download
page is our primary source of new customers, we need to make sure it is seen.
## Will premium improve my download speed?
No, the download speed is limited by the stability of the connection between
your computer and pixeldrain's servers. If free downloads are slow (and you have
not exceeded your download limit), then premium will not improve your download
speed. Premium only increases how much you can download, not how fast.
If you want to know your maximum download speed from pixeldrain's servers you
can use our [speedtest](/speedtest). The speedtest will always download at the
fastest speed possible, even if your download limit has been exceeded.
In order to keep pixeldrain affordable we use the cheapest hosting available.
That means that the quality of our network is not always the best. It's possible
that your ISP has a bad connection to our ISP which can cause bottlenecks. We
are always working on improving our connectivity.
## Is pixeldrain available in every country?
I strive to make pixeldrain as accessible as possible to everyone. Pixeldrain
does not block access from any country or network. Some countries have very
restricted internet connectivity though. Pixeldrain has been blocked in some
locations in the past and remains blocked in other locations.
The reasons for blocking pixeldrain are usually not clear. The countries that
block access to pixeldrain rarely specify a reason. When a new block happens I
always reach out to the ISP or government doing the blocking, but these entities
are very hard to reach and they rarely reply. If your ISP blocks pixeldrain
**please call them and ask them why pixeldrain is blocked**. ISPs always listen
better to their own customers than website operators.
I have a survey that you can use to notify me when an ISP blocks access to
pixeldrain. If you are having trouble accessing pixeldrain please [fill out the
survey](https://forms.gle/jThCp5S6xi49w2KP7).
Usually a website block can be circumvented by using a different DNS provider.
The DNS provider is a service that translates website addresses (like
pixeldrain.com) into IP addresses that can be used to connect to a website.
These servers are usually operated by your ISP and can be used to censor or
monitor your browsing.
Pixeldrain also has alternative domain names which might not be blocked. These
are [pixeldrain.net](https://pixeldrain.net) and
[pixeldra.in](https://pixeldra.in). Note that your session cookie is only valid
for one domain name. If you use these alternative domains you will have to log
in to them as well.
### DNS Providers which don't block pixeldrain
You can find a guide for how to change your DNS server on Google. Just search
for 'change dns server windows 11', or whichever operating system you use.
| Provider | IPv4 addreses | IPv6 addresses |
|------------|--------------------------|--------------------------------------------|
| Cloudflare | 1.1.1.1, 1.0.0.1 | 2606:4700:4700::1111, 2606:4700:4700::1001 |
| Quad9 | 9.9.9.9, 149.112.112.112 | 2620:fe::fe, 2620:fe::9 |
| Google | 8.8.8.8, 8.8.4.4 | 2001:4860:4860::8888, 2001:4860:4860::8844 |
### Countries where pixeldrain is blocked
From the availability survey I have gathered that pixeldrain is currently
blocked in the following locations:
* The Philippines (since 2022-03). I have reached out to PLDT about a dozen
times but they never answer.
* Egypt (since 2023-03). I have tried reaching out to WE Telecom, but their
website is not available outside egypt and their support address is bouncing
my mails.
* Italy (since 2024-01). Tried reaching out to their communications office and
police multiple times, never an answer.
* India (since 2024-04). Was unable to find a contact address anywhere.
If you live in any of these locations and are having trouble accessing
pixeldrain **please contact your ISP**. I am ready to comply with whatever
demands they have, I just want my website to be accessible again.
### Statement regarding the Italy block
I have been working on the blocking situation in Italy, but there has been no
progress. I have contacted every phone number or e-mail address I could find.
All my e-mails are ignored and half of the phone numbers are out of service and
the other half connect to (very impatient) people who don't speak english.
I do not know what else I can do to make more progress on this situation. I have
exhausted my options. Is there anyone in Italy or who speaks Italian who would
be willing to help? I need to get in contact with the Polizia Postale, which has
been blocking access to pixeldrain.com nationwide for well over a year now.
Not only are they blocking access to the site (which is bad enough), they are
also spreading a terrifying and misleading image of what pixeldrain is being
used for. This is seriously affecting the reputation of my business and myself.
I'd really like to get this resolved. I am also willing to offer a financial
compensation for anyone who can help me get this resolved. If you think you can
help, please contact me [on Discord](https://discord.gg/UDjaBGwr4p). My username
is Fornax.
## Why can't I find pixeldrain links on Google?
Files on pixeldrain used to be searchable with search engines if they were
indexed. People often accidentally got files indexed which were not supposed to
be public. For that reason I disabled search indexing on all pixeldrain files.
This protects the privacy of pixeldrain users and helps with preventing
information leaks.
## How does the affiliate program work?
Pixeldrain's affiliate program is a way to earn pixeldrain credit by driving
traffic to pixeldrain. The way it works is that you send people your affiliate
link, if someone accepts to be your affiliate then their active subscription
will earn you pixeldrain credit. The affiliate program is opt-in and fully
transparent. Users will always be notified when their affiliate account is
updated. You can update who you are sponsoring by editing the affiliate name on
your [user settings page](/user/settings).
Here is a summary of all the rules and limitations:
* Each paying customer using your affiliate code will earn you €0.50 in prepaid
credit every month. The credit is added to your account on a daily basis, as
you can see on the [transactions page](/user/prepaid/transactions).
* Sponsoring someone with an affiliate code does not cost you any extra money.
The resulting fee comes out of pixeldrain's pockets.
* You can only earn pixeldrain credit with the affiliate program. There is no
cash out feature.
* When someone who is using your affiliate code cancels their plan, you will
also stop receiving rewards.
* You don't need an active subscription to gain credit through the affiliate
program. You need a positive balance of at least €1 to activate the prepaid
plan.
Some fun facts:
* You only need two affiliates to offset pixeldrain's base subscription fee.
* Each sponsoring user is effectively equal to 125 GB of storage space or 500 GB
of bandwidth usage per month.
* You cannot sponsor yourself.
## Is there a clean pixeldrain logo I can use?
Yes, here's a high resolution pixeldrain logo with text. The font is called
Orbitron, it was designed by Matt McInerney and uses the Open Font License.
<img src="/res/img/pixeldrain_high_res.png" style="max-width: 100%; height: 80px;" /><br/>
And here's a vector version of just the icon:
<img src="/res/img/pixeldrain.svg" style="max-width: 100%; height: 80px;" /><br/>
## Can I advertise on pixeldrain?
No.
## Support
If you have more questions please try asking them in our [support forum on
Discord](https://discord.gg/UDjaBGwr4p). Pixeldrain is a one-man operation, I
can't answer all the e-mails I get. By asking your questions on Discord there's
a chance that someone else can help you. I am also active on Discord myself.

View File

@@ -1,143 +0,0 @@
# Content policy
[TOC]
## Disallowed contents
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. For copyright reports we need a formal DMCA takedown request
originating from the copyright holder or a representative. See the chapter
[E-Mail reporting rules](#toc_2) below. When sending a copyright infringement
notice to our abuse address, please state clearly that it is a copyright
infringement notice so that we can properly detect the type of report. Using
words like "theft" or "stolen" won't be detected because they are too vague.
* **Abuse of minors** - Videos, images or audio fragments depicting abuse or
inappropriate touching of minors will be removed. Users uploading this type of
content will receive a permanent account and IP address ban.
* **Zoophilia** - Videos, images or audio depicting abuse or inappropriate
touching of animals.
* **Terrorism** - Videos, images or audio fragments which promote and glorify
acts of terrorism. This category is only for material which promotes
terrorism. Material which shows terrorism (torture, murder) should be reported
under the _gore_ category.
* **Gore** - Graphic and shocking videos or images depicting severe harm to
humans (or animals).
* **Malware and computer viruses** - Software programs designed to cause harm to
computer systems. Please attach some proof that the file actually contains
malware, like a VirusTotal scan result. If no proof is provided then we will
assume the report is invalid.
* **Doxing** - Publishing private information about individuals or
organisations. This includes publicly sharing address information, ID scans or
login credentials which are not supposed to be public. Please [read about what
doxing really means](https://en.wikipedia.org/wiki/Doxing) before filing a
report in this category. If the file is content that you produce and sell,
then it does not fall under the doxing category. In that case you should file
a copyright claim.
* **Revenge porn** - The distribution of sexually explicit images or videos of
individuals without their consent. This category _does not apply_ for material
which is sold online for money. In that case you should file a copyright
claim. Please [read about what revenge porn really
means](https://en.wikipedia.org/wiki/Revenge_porn) before filing a report in
this category.
Violating these rules will result in your IP address being banned from uploading
to pixeldrain.
If you have found content which falls in any of these categories on pixeldrain
please report it by sending an e-mail to
[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com). Please state clearly in
which category the content falls, and don't forget to include a link to the
content itself in the e-mail. When reporting links through e-mail pay attention
to the rules described below.
## E-Mail reporting rules
Pixeldrain's abuse handling process has been largely automated. Messages sent to
[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com) are automatically scanned
for pixeldrain links and processed. The first report we receive from a sender is
manually reviewed. If the report is approved then your e-mail address will be
added to our whitelist and all following messages are processed automatically.
For this to work efficiently we have to set some requirements on the mails we
receive:
* Messages are categorized based on their contents. Make sure the report
contains a description of the type of content and that it mentions one of the
abuse categories listed above. The abuse mailbox only accepts reports written
in English.
* Do not add attachments to your e-mail reports. Only the e-mail body is checked
for download links. The message scanning system will not check your
attachments, download links within the attached files are not detected.
* Do not obfuscate the pixeldrain links. The reported download links need to be
complete and valid.
* The e-mail must include a
[Message-ID](https://en.wikipedia.org/wiki/Message-ID) header. The Message-ID
is used to reference messages in our system, mails without a Message-ID are
not processed.
* The abuse system uses e-mail addresses for authentication so we need to be
wary of [message spoofing](https://en.wikipedia.org/wiki/Email_spoofing). To
combat this we require both
[DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) and
[SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework) validation before
we can accept e-mails. If either of these checks fail we assume the message
was spoofed and it goes straight to the spambox. If your message is vulnerable
to spoofing then the bot will not reply to your message because we don't want
to send e-mails to spoofed addresses.
* Only send abuse reports to
[abuse@pixeldrain.com](mailto:abuse@pixeldrain.com). Messages sent to any
other e-mail address are ignored.
* Do not repeatedly send reports about files which have already been removed in
the past. We will block your e-mail address if this happens.
If you are not sure if your mailserver is configured correctly, then you can try
the spam test at [mail-tester.com](https://www.mail-tester.com/). Send an e-mail
to the address listed on this site and it will tell you if your mailserver is
configured right. Pay attention to the SPF, DKIM and DMARC results.
If your abuse report is rejected for one of the above reasons then you will
receive a reply with instructions on how to fix it.
## Police requests
In the case of official police investigations, pixeldrain can share user
details when requested. Please send all police data requests to
[police@pixeldrain.com](mailto:police@pixeldrain.com).
The following types of data are tracked in the pixeldrain database and can be
shared depending on the type of the case. Please specify which fields you
require in the investigation:
* File upload date and time (also publicly available on the download page)
* File upload IP address
* Username of the user who uploaded the file
* E-mail address of user
* Active login sessions of user
* User-agent of user
* IP addresses of active sessions of user
* Registration date of user
The severity and sensitivity of the request will be chosen at our own
discretion. In cases where the request is considered not serious or unlawful, we
reserve the right to deny the request for information.
## Disclaimer
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.

View File

@@ -1,54 +0,0 @@
# Acknowledgements
## Software used
* [Go](https://golang.org/)
* [ScyllaDB](https://www.scylladb.com/)
* [Nginx](https://www.nginx.com/)
* [Ubuntu Server edition](https://ubuntu.com/)
* [Debian](https://www.debian.org/)
## Programming libraries
* [scylladb/gocql](https://github.com/scylladb/gocql) (database communication)
* [scylladb/gocqlx](https://github.com/scylladb/gocqlx) (database communication)
* [BurntSushi/toml](https://github.com/BurntSushi/toml) (server configuration)
* [klauspost/reedsolomon](https://github.com/klauspost/reedsolomon) (reed-solomon erasure coding)
* [julienschmidt/httprouter](https://github.com/julienschmidt/httprouter) (request routing)
* [gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) (MIME type detection)
* [disintegration/imaging](https://github.com/disintegration/imaging) (image thumbnail generation)
* [ffmpeg](https://ffmpeg.org/) (video thumbnail generation)
* [gorilla/websocket](https://github.com/gorilla/websocket) (websocket support on file viewer)
* [shopspring/decimal](https://github.com/shopspring/decimal) (currency handling)
* [jhillyerd/enmime](https://github.com/jhillyerd/enmime) (e-mail parser)
* [russross/blackfriday](https://github.com/russross/blackfriday) (markdown renderer)
* [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) (HTML sanitizer)
* [j-muller/go-torrent-parser](https://github.com/j-muller/go-torrent-parser) (torrent file parser)
### Web framework
* [Svelte](https://svelte.dev/)
## Other resources
* [The map image](/res/img/map.webp) on the home page is from [Wikimedia
Commons](https://en.wikipedia.org/wiki/File:A_large_blank_world_map_with_oceans_marked_in_blue.PNG)
and is licensed under the GNU Free Documentation License.
* [The background image](/res/img/inflating_star.webp) on the home page is by
[NASA Goddard](https://images.nasa.gov/details-GSFC_20171208_Archive_e000383)
and is [allowed to be used
commercially](https://www.nasa.gov/nasa-brand-center/images-and-media/).
## Security work
* 2020-12-06 Security researcher Arian Firoozfar reported a cross-site
scripting vulnerability on the file viewer page. The issue was fixed the
following day. On the 26th another XSS vulnerability was found on the account
settings page, it was fixed later that day.
* 2017-12-04 Security researcher Hangyi reported a cross-site scripting
vulnerability on the file viewer page. The issue was fixed on the 6th.
If you have discovered a security issue in pixeldrain please disclose it
responsibly at [support@pixeldrain.com](mailto:support@pixeldrain.com). We do
not have a bug bounty program.

View File

@@ -1,6 +1,6 @@
# API documentation
Methods for using pixeldrain programmatically.
Methods for using Nova programmatically.
## Authentication
@@ -16,7 +16,7 @@ Example usage in JavaScript:
```js
const resp = await fetch(
"https://pixeldrain.com/api/user/files",
"https://nova.storage/api/user/files",
headers: {
"Authorization": "Basic "+btoa(":"+api_key),
// The btoa function encodes the key to Base64
@@ -39,20 +39,20 @@ URL.
## curl example
To upload files to pixeldrain you will need an API key. Get an API key from the
To upload files to Nova you will need an API key. Get an API key from the
[API keys page](/user/api_keys) and enter it in the command. Replace the example
API key here with your own:
`curl -T "file_name.txt" -u :5f45f184-64bb-4eaa-be19-4a5f0459db49
https://pixeldrain.com/api/file/`
https://nova.storage/api/file/`
## Form value order
I recommend you put files at the end of every file upload form. By doing this
the pixeldrain server can respond to malformed requests before the file upload
the Nova server can respond to malformed requests before the file upload
finishes and this may save you a lot of time and bandwidth when uploading large
files. Make sure your HTTP client has support for premature responses,
pixeldrain uses them a lot. If the server responds before your request is
Nova uses them a lot. If the server responds before your request is
finished it will always indicate an error and you may abort the connection.

View File

@@ -161,11 +161,11 @@ Warning: If a file is using too much bandwidth it can be rate limited. The rate
limit will be enabled if a file has three times more downloads than views. The
owner of a file can always download it. When a file is rate limited the user
will need to fill out a captcha in order to continue downloading the file. The
captcha will only appear on the file viewer page (pixeldrain.com/u/{id}). Rate
captcha will only appear on the file viewer page (nova.storage/u/{id}). Rate
limiting has been added to prevent the spread of viruses and to stop hotlinking.
Hotlinking is only allowed when files are uploaded using a Pro account.
Pixeldrain also includes a virus scanner. If a virus has been detected in a file
Nova also includes a virus scanner. If a virus has been detected in a file
the user will also have to fill in a captcha to download it.
### Parameters

View File

@@ -89,11 +89,11 @@ Warning: If a file is using too much bandwidth it can be rate limited. The rate
limit will be enabled if a file has ten times more downloads than views. The
owner of a file can always download it. When a file is rate limited the user
will need to fill out a captcha in order to continue downloading the file. The
captcha will only appear on the file viewer page (pixeldrain.com/u/{id}). Rate
captcha will only appear on the file viewer page (nova.storage/u/{id}). Rate
limiting has been added to prevent the spread of viruses and to stop direct
linking.
Pixeldrain also includes a virus scanner. If a virus has been detected in a file
Nova also includes a virus scanner. If a virus has been detected in a file
the user will also have to fill in a captcha to download it.
### Parameters

View File

@@ -16,7 +16,7 @@ POST body should be a JSON object, example below. A list can contain at most
#### Example
```
{
"title": "My beautiful photos", // Defaults to "Pixeldrain List"
"title": "My beautiful photos", // Defaults to "Nova List"
"anonymous": false / true, // If true this list will not be linked to your user account. Defaults to "false"
"files": [ // Ordered array of files to add to the list
{

View File

@@ -1,138 +0,0 @@
# Pixeldrain business plan
## The problem
Sending large files to people is still hard. In the last 20 years we have made
little progress in this area.
## The competition
There is no shortage of file sharing sites on the internet. Some of them are
very large, but none of them are perfect.
### WeTransfer
WeTransfer is the world's 410'th largest website. It gets an estimated 140
million unique visitors per month. WeTransfer is loved by its users, but it has
many flaws in my opinion.
* ✅ Minimalistic user interface. Easy to understand
* ✅ Ads do not get in the way
* ❌ Slow and hard to navigate website
* ❌ Unclear what the limits are
* ❌ No way to view video, audio and images online
* ❌ Trackers: Google Tag Manager, no privacy for users
* ❌ Pro plan is expensive (€12 / m)
* ❌ Low limits. Files expire after only 1 week and max file size is only 2 GB
### Zippyshare
Zippyshare is the world's 850'th largest website. It gets around 56 million
unique visitors per month. Despite its size and popularity I think it should be
easy to overtake. Users are not very loyal and it mostly gets used for piracy.
* ✅ Easy to use
* ✅ Clearly defined limits
* ✅ File retention is 30 days after the last download, same as pixeldrain
* ✅ Very fast download speed
* ✅ No limits on how many times a file can be downloaded
* ✅ Viewing files online is possible
* ❌ Low size limit (500 MB per file)
* ❌ Outdated design (looks like a website from 15 years ago)
* ❌ Horrible ads on download page (push notifications, popunders, clickjackers,
fake download buttons). Not the kind of thing you'd want to send to your
family and friends
* ❌ No premium plan (at least not well advertised)
## The solution
Pixeldrain presents itself as the best of both worlds:
* ✅ A clear user interface which guides the user through the process of
uploading and sharing files
* ✅ Fast content delivery servers around the world make sure your files are
always right around the corner
* ✅ Clearly defined and liberating file size limit of 5 GB per file. The
highest in the industry
* ✅ Files stay around until 30 days after the last time it's downloaded
* ✅ Because pixeldrain does not use trackers we can't see what you're sharing,
and neither can Google or Facebook. This makes pixeldrain the ultimate
solution for privacy-minded people
## Monetization
### Pixeldrain Pro
A subscription plan of €12 per _year_ (that's 10x cheaper than WeTransfer!).
This amount of money should be enough to cover all the usage costs for most
users. With this you will get:
* File size limit will be raised from 5 GB to 10 GB.
* File expiration will be increased from 30 days to 60 days after the last
download.
* No ads on files you share, the download page will be completely clean. This
results in a faster and cleaner experience for people who receive the shared
files. Which should make them more interested in the product.
* No trackers on the site, everything your browser requests will come from
pixeldrain's servers. This caters to the privacy conscious audience. This
market has been booming lately, just take a look at all the VPN companies and
all the social media scandals.
This plan does not diable the rate limiting which kicks in when a file has 3
times more downloads than views. So direct linking is not an option. To solve
that we have to second option.
Pixeldrain doesn't actually need people to subscribe in order to survive. But I
think turning off ads and trackers is a basic right that every website should
have. This plan only really starts getting profitable when more than 10 000
people sign up for it. I will still have to look for advertising deals.
### Pixeldrain Enterprise
We will also cater to business owners and professional file sharers with a
pay-as-you-go subscription. This subscription will charge €6 per TB for storage
and €2 per TB for bandwidth respectively. This is extremely cheap for content
delivery standards. For this money you will get a product which does not really
offer what a real CDN would (automatic file caching and load reduction), but you
will get an easy and cheap method for sharing huge files to enormous amount of
people. And that at one tenth of the cost of a regular CDN.
Apart from that you will also get:
* Access to pixeldrain's Buckets feature. This allows you to create public
directories which you can use to share large datasets selectively. And easily
manage your files through a blazing fast web UI or even SFTP.
* Whitelabel download pages. Brand your download pages with custom colours,
icons and background graphics.
* You are completely shielded from all of pixeldrain's limits. Rate limiting
will be turned off and you will be able to directly link to your files
instead of having to go through a separate download page.
* A billshock prevention mechanism will prevent people from repeatedly
downloading your files and increasing your usage.
## Spreading the word
And this is the beautiful part. Pixeldrain's download page already sees millions
of users _per day_! I don't have to spend a ton of money on marketing and
advertising. All I have to do to let people know about all this is place some
witty ads on my own download page.
* Files expiring too soon? Pixeldrain Pro, only €1 per month!
* Tired of those annoying ads? Pixeldrain Pro, only €1 per month!
* Are your files too large for other sites too handle? Pixeldrain Pro, only €1
per month!
* Are Google and Facebook following you around the internet? Pixeldrain Pro,
only €1 per month!
* Et cetera
## TODO: How pixeldrain can afford to be cheaper

View File

@@ -1,237 +0,0 @@
# Filesystem Guide
Pixeldrain has an experimental filesystem feature. It can be accessed from any
account with a paid subscription (Patreon or Prepaid) by going to
[pixeldrain.com/d/me](/d/me).
* **IMPORTANT**: The filesystem is still in development. This means that it's
not finished yet. While the filesystem seems stable now and I am using it
personally too, you are strongly advised to keep backups of anything you
upload here.
* If you experience any issues while using the filesystem, feel free to discuss
them on [the Discord community](https://discord.gg/TWKGvYAFvX).
Contents
[TOC]
## Pricing
Every time you create or remove a file your account's storage usage will be
updated. This can take some time. If your account's storage is full you will no
longer be able to upload anything to the filesystem.
The Pro subscription has a storage limit of 2 TB. It doesn't show on the profile
page because it's calculated differently from the other plans, but it is there.
For Prepaid plans the storage is charged at €4 per TB per month. You can view
your usage in the [transaction log](/user/prepaid/transactions).
Downloads from the filesystem are charged at €1 / TB for prepaid. With Patreon
plans there's a monthly limit. If you turn hotlinking off in the account
settings then other people will use their own daily download limit. Otherwise
they will use your account's transfer limit.
### Metadata storage cost
Files and directories stored in the filesystem also use space in the database.
To account for this we added an extra rule when counting space usage. All files
under 1000 bytes will be rounded up to 1000 bytes. All directories in the
filesystem will also use 1000 bytes of storage space. There are a couple of
reasons why this rule was put into place:
* Storing metadata like file names and permissions costs space in the database.
Database storage is much more expensive than bulk data storage.
* If directories were free to store, you could theoretically store unlimited
amounts of data in directory names and not pay for it.
* Accounts with lots of small files and directories slow down database
maintenance processes.
* Directories on other filesystems like ext4 also use storage space. Typically
that's 4 KiB, so 1000 bytes is still low in comparison.
To put the pricing into perspective: Storing 1000 bytes on pixeldrain costs
€0,000000004. For €1 a month you can store 250 million directories.
## Free download limit
The pixeldrain filesystem uses the same download limit as the regular files on
pixeldrain. The only difference is that the limit is 1 GB higher. So while you
can freely download up to 5 GB per day from regular pixeldrain files, you can
download up to 6 GB per day from the filesystem. When the limit is exceeded the
speed is limited to 1 MiB/s like usual.
If you want to embed pixeldrain files on your own website, distribute direct
download links or share files that are larger than the download limit then you
should turn the 'Hotlinking' option on. Otherwise people will have trouble
downloading your files.
## Directory sharing
Files in the the filesystem are private by default. Only you can access them
from your own account. Files and directories can be shared by clicking the
`Share` button in the toolbar while inside the directory, or by clicking the
pencil icon next to the directory in the file viewer.
Shared directories and files will have a shared icon next to them in the file
manager. Clicking that icon will open the shared link. You can also copy the
shared link directly with the `Copy link` button in the toolbar.
If a shared file gets reported for breaking the [content policy](/abuse) your
ability to share files from your account may be taken away.
## Limits
Here is a quick overview of the filesystem's limits:
* Max 10000 files per directory
* Max file size is 100 GB
* File/directory names can be up to 255 bytes long
* Path names can be up to 4095 bytes long
* You can have a maximum of 64 nested directories
* The filesystem does not support hard or symbolic links, this might change
later
When traversing a path, pixeldrain requests one directory at a time from the
database. This means that filesystem operations will get slower the more nested
directories you have. Keep that in mind when organizing your files.
## Importing files
It's possible to import files from your account's file list to your filesystem.
To do so, navigate to a directory in your filesystem, click the `Import files`
button on the toolbar. It's on the right side, between the `Create directory`
and `Edit files` buttons. You will be prompted to select the files you would
like to import. After selecting the files click `Add` and they will be added to
your filesystem.
## Client integrations
There are two ways to access your filesystem from outside the web interface.
### Rclone
Rclone has built in support for the pixeldrain filesystem starting with version
1.68. Check out [the rclone website](https://rclone.org/pixeldrain/) for
documentation. You can install rclone [from the
site](https://rclone.org/downloads/). It's also available in most software
repositories.
A few example use cases of rclone are:
* Mount pixeldrain as a network drive with [rclone
mount](https://rclone.org/commands/rclone_mount/) (instructions below)
* Create a backup of your local storage with [rclone
sync](https://rclone.org/commands/rclone_sync/)
* Perform a two-way sync with [rclone
bisync](https://rclone.org/commands/rclone_bisync/) (bisync is experimental
tech, don't use with important data)
#### Rclone upload performance issue
Under some circumstances rclone has trouble with reaching full upload speed when
using the HTTP2 protocol (which is enabled by default). This is due to a [bug in
the Go runtime](https://github.com/golang/go/issues/37373). If you are
experiencing this then it might help to add the `--disable-http2` flag to your
rclone commands.
#### Rclone mount systemd service example
To automatically mount your pixeldrain when logging in to your Linux OS you can
use a systemd user service.
First you must configure an rclone remote with the name `Pixeldrain`. This will
be the name of the network drive as well. You can choose a different name if you
want to.
1. Run `rclone config` to start the interactive configuration prompt.
2. Press `n` to create a new remote.
3. Enter the name `Pixeldrain`, or a different name if you want.
4. When asked which storage provider you want to use enter `pixeldrain`.
5. Follow the rest of the instructions.
Create a text file with these contents at the path
`$HOME/.config/systemd/user/rclone@.service`. You may have to create the parent
directories yourself.
```
[Unit]
Description=rclone: Remote FUSE filesystem for cloud storage config %i
Documentation=man:rclone(1)
After=network-online.target
Wants=network-online.target
AssertPathIsDirectory=%h/%i
StartLimitBurst=5
[Service]
Type=simple
ExecStart=rclone mount \
--config=%h/.config/rclone/rclone.conf \
--vfs-cache-mode full \
--vfs-cache-max-age 720h \
--vfs-cache-min-free-space 50G \
--vfs-write-back 10s \
--dir-cache-time 10m \
--log-level INFO \
--transfers 10 \
--file-perms 0700 \
--dir-perms 0700 \
%i: %h/%i
KillSignal=SIGINT
TimeoutStartSec=600
Restart=on-failure
[Install]
WantedBy=default.target
```
Once the file is in place, reload your systemd config with `systemctl --user
daemon-reload`. Then you can start your network drive with `systemctl --user
enable rclone@Pixeldrain.service --now`, where `Pixeldrain` is the name of your
rclone remote (replace with the name of your own remote if necessary). This will
create a directory called `Pixeldrain` in your home which will contain your
network drive. If it doesn't work, you can check the logs with `journalctl
--user -u rclone@Pixeldrain`.
If you can't get it to work you can always ask for help on our [Discord
community](https://discord.gg/TWKGvYAFvX).
### FTPS
The filesystem also supports FTPS, both anonymously and with an account. The FTP
server is hosted at `pixeldrain.com` on port `990`. The encryption mode used is
`Implicit FTP over TLS`. Here is an example configuration in FileZilla:
![FTP configuration](/res/img/misc/ftp_login.webp)
When using FileZilla please make sure you have the "Data transfer mode" set to
Binary. If you do not do this FileZilla will corrupt your files if it does not
detect the file type correctly. This can happen if you have files without a file
extension. Data transfer mode can be found in the Settings screen under
Transfers > FTP: File Types. Set to "Binary" to prevent FileZilla from
corrupting your data.
There are two different ways to log in to the FTP server:
#### Read-write personal directory
To connect to your personal directory you need to enter your account's username
as username in the FTP client. The password needs to be an API key from the [API
keys page](/user/api_keys). If you connect now you will be able to access your
personal directory (called `/me`). Here you can upload and download to your
heart's desire.
#### Read-only shared directory
To access a shared directory in read-only mode you need to enter the directory
ID as username in your FTP client. The directory ID can be found at the end of a
shared directory URL. Example: `https://pixeldrain.com/d/abcd1234`, in this case
`abcd1234` is the directory ID. The ID will always be 8 characters long and is
case-sensitive. The password must be left empty
## Filesystem API
There is a public filesystem REST API, but it is not documented currently. The
best reference for how to use the API would be the TypeScript client which the
website uses as well. That can be found [on
GitHub](https://github.com/Fornaxian/pixeldrain_web/blob/master/svelte/src/filesystem/FilesystemAPI.mts).

View File

@@ -1,127 +0,0 @@
# Sia hosting guidelines
Pixeldrain uses [Sia](https://sia.tech) to offload files which are not requested
often, but still need to be kept. Sia is a free storage market where any host
can choose their own pricing. Because of this the users of the network need to
be careful when choosing the hosts to make contracts with.
Because pixeldrain is fairly cost-constrained we are forced to set some hard
requirements on storage and bandwidth pricing for Sia hosts.
## Rates
We will only make contracts with hosts that fullfill all these requirements.
Keep in mind that these are maximums, you are allowed to go lower.
{{$price := .PixelAPI.GetSiaPrice}}
| Requirement | Max rate EUR | Max rate SC |
|--------------------------|--------------|-------------|
| Storage price per month | € 1.50 / TB | {{ div 1.5 $price | formatSC }} / TB |
| Download price | € 2.00 / TB | {{ div 2.0 $price | formatSC }} / TB |
| Upload price | € 0.50 / TB | {{ div 0.5 $price | formatSC }} / TB |
| Contract formation price | € 0.10 | {{ div 0.1 $price | formatSC }} |
<sup>
Based on exchange rates from Kraken.
</sup>
This may seem low, but keep in mind that these prices are before redundancy. We
have to upload all our data three times to the Sia network in order to reach
high availability. If you multiply everything by three it becomes much more
reasonable.
We also can't guarantee that your host will be picked when it fulfills these
requirements. If there is enough supply we will only pick the most reliable
hosts available.
Other settings we pay attention to:
| Setting | Recommended value |
|-----------------------|-------------------|
| Max contract duration | At least 4 months |
| Proof window duration | 1 day |
| Download batch size | At least 16 MiB |
| Revision batch size | At least 16 MiB |
## Tips and tricks for becoming a better host
### Use a stable Linux or BSD-based operating system
Sia is known to run better on Linux or BSD based operating systems. Windows is
discouraged due to I/O reliability issues. Windows often sacrifices reliability
for better performance, because of this crashes are more common on Windows and
also have a greater chance of resulting is data loss. Forced updates and other
interruping system processes are also likely to harm hosting uptime and
performance.
We can recommend Debian, CentOS or Ubuntu LTS for hosting. These are systems
which are known to be able to run uninterruped for decades. They are also
regularly patched with security updates which don't even require restarting most
of the time. This makes these systems perfect for the role of hosting on Sia.
### Enable TCP BBR and other network stack optimizations
BBR is a new congestion control agorithm which dramatically decreases the time
needed for a TCP connection to ramp up to maximum speed. It also contains
improvements to counter other problems like router buffer bloat which causes
network latency spikes. Here's an [in-depth analysis of the benefits of enabling
BBR](https://blog.apnic.net/2017/05/09/bbr-new-kid-tcp-block/).
To enable BBR you need you have kernel version 4.9 or higher. See your kernel
version with `uname -a`. On Ubuntu you can upgrade to a newer kernel by
[enabling HWE](https://wiki.ubuntu.com/Kernel/LTSEnablementStack).
Create a file called `/etc/sysctl.d/60-bbr.conf` with the following contents:
```
net.core.default_qdisc = fq_codel
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_notsent_lowat = 16384
net.ipv4.tcp_slow_start_after_idle = 0
```
After doing that you can run `sysctl -p` or reboot to apply the changes. Verify
that it's working with this command: `sysctl net.ipv4.tcp_congestion_control`.
It should return `bbr`.
Here's a more in-depth [guide to the configuration of the linux network
stack](https://www.cyberciti.biz/cloud-computing/increase-your-linux-server-internet-speed-with-tcp-bbr-congestion-control/).
### Use Sia Host Manager to configure your host
SiaCentral's [Host Manager](https://siacentral.com/host-manager) is a great tool
for monitoring and configuring your host. It explains all the settings in
detail, gives an option to set prices in any currency you like and gives
detailed insights into your contracts and revenue stream.
### Sign up for SiaStats host alerts
When your host is configured properly SiaStats will monitor its uptime and
performance. These stats are important for renters to discover good hosts and to
get an overview into the state of the hosting network.
If your host has been online for a while it will show up on SiaStats' [hosting
page](https://siastats.info/hosts). If you search for your host there will be an
option to sign up for hosting alerts.
### IPv6 capability is encouraged
Pixeldrain makes heavy use of IPv6 across its systems. We do this because we
believe that IPv6 is a critical component for the free internet. The old IPv4
requires terrible hacks like NAT to work at a large scale. IPv4 addresses are
also scarce and expensive to rent. All this money is thrown away on a legacy
system for which a replacement has already existed for over a decade. NAT limits
the growth of peer-to-peer software by making it impossible for applications to
communicate freely over the internet. Instead we need to add more hacks on top
like port forwarding to make it work. This has harmed the growth of the open
internet a lot over the decades and will harm it more if we keep going like
this.
So enable IPv6. If you don't have IPv6, call your ISP and ask them why not.
<div style="margin-top: 100px; height: 128px; text-align: center;">
<a href="https://sia.tech/">
<img style="height: 100%;" src="/res/img/misc/built-with-Sia-mono.svg" alt="Built with Sia"/>
</a>
</div>

View File

@@ -1,18 +0,0 @@
# Limits
We need to limit the features of pixeldrain to avoid abuse and encourage you to
get a subscription. Here's an overview of all limits which are currently in
place on pixeldrain.
| | Anonymous | Registered | Pro | Prepaid |
|-----------------------|--------------|-----------------|---------------|---------------------|
| Download speed | 2 MiB/s | 2 MiB/s | Unlimited | Unlimited |
| File storage | Unlimited | 500 GB | 1 TB | Unlimited (€4 / TB) |
| File expiry | 30 days | 30 days | 90 days | Unlimited |
| File size | 10 GB | 10 GB | 20 GB | 20 GB |
| File downloads | 1000 / day | Unlimited | Unlimited | Unlimited |
| Free data transfer | 100 GB / day | Unlimited | Unlimited | Unlimited |
| Premium data transfer | No | 10 GB / month | 1 TB / month | Unlimited (€1 / TB) |
| Video streaming | No | Within data cap | Yes | Yes |
| Ad-free file viewing | No | No | Yes | Yes |
| File viewer branding | No | No | From €8/month | Yes |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -7,7 +7,7 @@
<body>
{{template "page_top" .}}
<header>
<h1>You broke pixeldrain</h1>
<h1>You broke Nova</h1>
</header>
<div id="page_content" class="page_content">
<section>
@@ -15,7 +15,7 @@
Great job.
</p>
<p>
Just kidding, something went wrong on the pixeldrain server and
Just kidding, something went wrong on the Nova server and
it's my fault. This can happen when the website is overloaded,
there is a problem with the software or there is a hardware
problem. When there is a large scale outage you can usually find

View File

@@ -1,145 +0,0 @@
{{ define "apps" }}<!DOCTYPE html>
<html lang="en">
<head>
{{template "meta_tags" "Apps"}}
<style>
.specs {
text-align: center;
border-bottom: 1px solid var(--separator);
}
</style>
</head>
<body>
{{template "page_top" .}}
<header>
<h1>Apps</h1>
</header>
<div id="page_content" class="page_content">
<section class="highlight_border" style="text-align: initial">
<h2>ShareX</h2>
<div class="specs">
Platform: Windows | License: GPL-3.0 |
<a href="https://getsharex.com">Website</a> |
<a href="https://github.com/ShareX/ShareX">GitHub</a>
</div>
<p>
<img src="/res/img/sharex.png" style="float: right; height: 6em; margin: 0.5em;" />
</p>
<p>
ShareX is a Screen capture, file sharing and productivity tool.
Pixeldrain is supported as a custom uploader. You can <a
href="https://getsharex.com/">get ShareX
here</a>.
</p>
<p>
You can download a custom ShareX uploader profile which uses
your pixeldrain account for uploading files on the <a
href="/user/connect_app?app=sharex">account apps page</a>.
</p>
<h3>Setting pixeldrain as default uploader</h3>
<p>
Download the uploader config and choose 'Open file'<br/>
<img src="/res/img/sharex_download.png" style="max-width: 100%;" /><br/>
Set pixeldrain.com as active uploader. Choose Yes<br/>
<img src="/res/img/sharex_default.png" style="max-width: 100%;" /><br/>
</p>
</section>
<br/>
<section class="highlight_border" style="text-align: initial">
<h2>Drainy</h2>
<div class="specs">
Platform: Windows, Linux, Android |
License: MIT |
<a href="https://github.com/ManuelReschke/go-pd-gui">GitHub</a>
</div>
<p>
<img src="/res/img/drainy.png" style="float: right; height:
6em; margin: 0.5em;" /> A simple tool for uploading files to
pixeldrain. Supports uploading to accounts, copying download
links to the clipboard and has an upload history screen.
</p>
</section>
<br/>
<section class="highlight_border" style="text-align: initial">
<h2 style="clear: both;">Pixeldrain Android</h2>
<div class="specs">
Platform: Android |
License: None |
<a href="https://github.com/wimvdputten/Pixeldrain_android">GitHub</a>
</div>
<p>
An Android app for uploading and sharing files on
pixeldrain. You can get a compiled APK package from the <a
href="https://github.com/wimvdputten/Pixeldrain_android/releases">
GitHub releases page</a>.
</p>
</section>
<br/>
<section class="highlight_border" style="text-align: initial">
<h2>go-pd</h2>
<div class="specs">
Platform: Linux, Mac OS, Windows (CLI) |
License: MIT |
<a href="https://github.com/ManuelReschke/go-pd">GitHub</a>
</div>
<p>
go-pd is a command line interface and client library for
pixeldrain created by Manuel Reschke. The CLI supports uploading
files anonymously, and uploading to user accounts. The client
library supports uploading, downloading and deleting files,
creating and viewing lists and user accounts. Compiled binaries
are available on the <a
href="https://github.com/ManuelReschke/go-pd/releases">
GitHub releases page</a>.
</p>
</section>
<br/>
<section class="highlight_border" style="text-align: initial">
<h2>go-pixeldrain</h2>
<div class="specs">
Platform: Linux, Mac OS, Windows (CLI) |
License: MIT |
<a href="https://jkawamoto.github.io/go-pixeldrain/">Website</a> |
<a href="https://github.com/jkawamoto/go-pixeldrain">GitHub</a>
</div>
<p>
go-pixeldrain is a command line interface for pixeldrain
created by Junpei Kawamoto. You can use it to upload and
download files and directories from your terminal. Compiled
binaries are available on the <a
href="https://github.com/jkawamoto/go-pixeldrain/releases">
GitHub releases page</a>.
</p>
</section>
<br/>
<section class="highlight_border" style="text-align: initial">
<h2>pdup</h2>
<div class="specs">
Platform: Linux, BSD, Mac OS (CLI) |
License: None |
<a href="https://github.com/Fornax96/pdup">GitHub</a>
</div>
<p>
pdup is a little bash script for uploading files to pixeldrain
from the terminal. It's available <a
href="https://github.com/Fornax96/pdup">from
GitHub</a>.
</p>
</section>
<section>
<h2>More apps</h2>
<p>
If you know more open source apps which work with pixeldrain
please send them to
<a href="mailto:support@pixeldrain.com">support@pixeldrain.com</a>.
</p>
</section>
</div>
{{template "page_bottom" .}}
</body>
</html>
{{ end }}

View File

@@ -1,5 +1,5 @@
{{define "meta_tags"}}
<title>{{.}} ~ pixeldrain</title>
<title>{{.}} ~ Nova</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<meta name="theme-color" content="#220735" />
@@ -7,20 +7,20 @@
<link id="stylesheet_layout" rel="stylesheet" type="text/css" href="/res/style/layout.css?v{{cacheID}}"/>
<link id="stylesheet_theme" rel="stylesheet" type="text/css" href="/theme.css"/>
<link rel="icon" sizes="32x32" href="/res/img/pixeldrain_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/pixeldrain_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/pixeldrain_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/pixeldrain_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/pixeldrain_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/pixeldrain_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/pixeldrain_196.png" />
<link rel="icon" sizes="32x32" href="/res/img/nova_32.png" />
<link rel="icon" sizes="128x128" href="/res/img/nova_128.png" />
<link rel="icon" sizes="152x152" href="/res/img/nova_152.png" />
<link rel="icon" sizes="180x180" href="/res/img/nova_180.png" />
<link rel="icon" sizes="192x192" href="/res/img/nova_192.png" />
<link rel="icon" sizes="196x196" href="/res/img/nova_196.png" />
<link rel="icon" sizes="256x256" href="/res/img/nova_256.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/res/img/nova_152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/res/img/nova_180.png" />
<link rel="shortcut icon" sizes="196x196" href="/res/img/nova_196.png" />
<meta name="description" content="Pixeldrain is a file transfer service, you
<meta name="description" content="Nova is a file transfer service, you
can upload any file and you will be given a shareable link right away.
pixeldrain also supports previews for images, videos, audio, PDFs and much more." />
Nova also supports previews for images, videos, audio, PDFs and much more." />
<meta name="keywords" content="file sharing, free file sharing, file transfer,
free file transfer, file hosting, free file hosting, hosting, file upload,
free file upload, uploading, send file, large file, free file sharing,
@@ -34,10 +34,10 @@ professional file transfer, cheap storage, cheap file storage, send large file,
send big file, send huge file, audio sharing, music sharing, audio upload,
music upload" />
<meta property="og:type" content="website" />
<meta property="og:title" content="{{.}} ~ pixeldrain" />
<meta property="og:site_name" content="pixeldrain" />
<meta property="og:title" content="{{.}} ~ Nova" />
<meta property="og:site_name" content="Nova" />
<meta property="og:description" content="Instant file and screenshot sharing." />
<meta property="og:url" content="http://pixeldrain.com/" />
<meta property="og:image" content="/res/img/pixeldrain_256.png" />
<meta property="og:url" content="http://nova.storage/" />
<meta property="og:image" content="/res/img/nova_256.png" />
<meta property="og:image:type" content="image/png" />
{{end}}

View File

@@ -57,12 +57,10 @@ function resetMenu() {
<footer>
<div class="footer_content">
<div style="display: inline-block; margin: 0 8px;">
Pixeldrain is a product by <a href="//fornaxian.tech" target="_blank">Fornaxian Technologies</a>
Nova is a product by <a href="//fornaxian.tech" target="_blank">Fornaxian Technologies</a>
</div>
<br/>
<div style="display: inline-block; margin: 0 8px;">
<a href="https://www.patreon.com/pixeldrain" target="_blank">{{template `patreon.svg` .}} Patreon</a> |
<a href="https://reddit.com/r/pixeldrain" target="_blank">{{template `reddit.svg` .}} Reddit</a> |
<a href="https://github.com/Fornaxian" target="_blank">{{template `github.svg` .}} GitHub</a> |
<a href="https://mastodon.social/web/@fornax" target="_blank">{{template `mastodon.svg` .}} Mastodon</a>
</div>

View File

@@ -6,14 +6,14 @@
<body>
{{template "page_top" .}}
<header>
<h1>Logging out of your pixeldrain account</h1>
<h1>Logging out of your Nova account</h1>
</header>
<div id="page_content" class="page_content">
<br/>
<form method="POST" action="/logout">
<button role="submit" class="button_highlight">
<i class="icon">logout</i>
Log out of pixeldrain on this computer
Log out of Nova on this computer
</button>
</form>
<br/>
@@ -23,7 +23,7 @@
We need you to confirm your action so we can be sure that you
really requested a logout. If we didn't do this, anyone (or any
website) would be able to send you to this page and you would
automatically get logged out of pixeldrain, which would be very
automatically get logged out of Nova, which would be very
annoying.
</p>
<p>

View File

@@ -66,7 +66,7 @@
</fieldset>
<br/><br/>
<iframe src="https://pixeldrain.com/u/Nygt1on4?embed" style="border: none; width: 800px; max-width: 100%; height: 600px; max-height: 100%; border-radius: 16px;"></iframe>
<iframe src="https://nova.storage/u/Nygt1on4?embed" style="border: none; width: 800px; max-width: 100%; height: 600px; max-height: 100%; border-radius: 16px;"></iframe>
</section>
</div>
{{template "page_bottom" .}}

View File

@@ -1,11 +1,11 @@
{
"name": "pixeldrain-web",
"name": "nova-web",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pixeldrain-web",
"name": "nova-web",
"version": "1.0.0",
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^6.2.1",

View File

@@ -1,5 +1,5 @@
{
"name": "pixeldrain-web",
"name": "nova-web",
"version": "1.0.0",
"type": "module",
"scripts": {

View File

@@ -49,10 +49,10 @@ let block_form = {
let message = "The following files were blocked:<br/>"
message += "<ul>"
jresp.files_blocked.forEach(file => {
message += "<li>pixeldrain.com/u/" + file + "</li>"
message += "<li>nova.storage/u/" + file + "</li>"
})
jresp.filesystem_nodes_blocked.forEach(file => {
message += "<li>pixeldrain.com/d/" + file + "</li>"
message += "<li>nova.storage/d/" + file + "</li>"
})
message += "</ul>"
@@ -73,7 +73,7 @@ onMount(() => {
<section>
<h2>File removal</h2>
<p>
Paste any pixeldrain file links in here to remove them
Paste any Nova file links in here to remove them
</p>
<div class="highlight_border">
<Form config={block_form}></Form>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount, tick } from "svelte";
import EmailReportersTable from "./EmailReportersTable.svelte";
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
import { loading_finish, loading_start } from "lib/Loading";
type Reporter = {

View File

@@ -5,7 +5,7 @@ import Chart from "util/Chart.svelte";
import { color_by_name } from "util/Util";
import ServerDiagnostics from "./ServerDiagnostics.svelte";
import PeerTable from "./PeerTable.svelte";
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
let graphEgress: Chart = $state()
let graphDownloads: Chart = $state()

View File

@@ -1,5 +1,5 @@
import { loading_finish, loading_start } from "lib/Loading";
import { check_response, get_endpoint } from "lib/PixeldrainAPI";
import { check_response, get_endpoint } from "lib/NovaAPI";
import hsl2rgb from "pure-color/convert/hsl2rgb";
import rgb2hex from "pure-color/convert/rgb2hex";

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
import { onMount } from "svelte";
import { formatDuration } from "util/Formatting";

View File

@@ -3,7 +3,7 @@ import { onMount } from "svelte";
import { formatDataVolume, formatDate } from "util/Formatting";
import SortButton from "layout/SortButton.svelte";
import { loading_finish, loading_start } from "lib/Loading";
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
interface Props {
user_id?: string;

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { loading_finish, loading_start } from "lib/Loading";
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
import { onMount } from "svelte";
import { formatDate } from "util/Formatting";

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import PixeldrainLogo from "util/PixeldrainLogo.svelte";
import NovaLogo from "util/NovaLogo.svelte";
import Button from "layout/Button.svelte";
import Euro from "util/Euro.svelte";
import { formatDataVolume } from "util/Formatting";
@@ -10,7 +10,7 @@ let button: HTMLButtonElement = $state()
let dialog: Dialog = $state()
let {
no_login_label = "Pixeldrain",
no_login_label = "Nova",
hide_name = true,
hide_logo = false,
style = "",
@@ -32,7 +32,7 @@ const open = () => dialog.open(button.getBoundingClientRect())
<div class="wrapper">
<button bind:this={button} onclick={open} class="button round" title="Menu" style={style}>
{#if !hide_logo}
<PixeldrainLogo style="height: 1.6em; width: 1.6em;"/>
<NovaLogo style="height: 1.6em; width: 1.6em;"/>
{/if}
<span class="button_username" class:hide_name>
{$user.username === "" ? no_login_label : $user.username}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import Button from "layout/Button.svelte";
import { fs_delete_all, type FSNode } from "lib/FilesystemAPI.svelte";
import { fs_trash, type FSNode } from "lib/FilesystemAPI.svelte";
import type { FSNavigator } from "filesystem/FSNavigator";
import { loading_finish, loading_start } from "lib/Loading";
@@ -25,7 +25,7 @@ const delete_file = async (e: MouseEvent) => {
try {
loading_start()
await fs_delete_all(file.path)
await fs_trash(file.path)
} catch (err) {
console.error(err)
alert(err)
@@ -61,10 +61,10 @@ const delete_file = async (e: MouseEvent) => {
<fieldset>
<legend>Delete</legend>
<p>
Delete this file or directory. If this is a directory then all
subfiles will be deleted as well. This action cannot be undone.
Move this file or directory to the trash. You can restore it or
permanently delete it from the <a href="/d/me/.Trash">trash bin</a>.
</p>
<Button click={delete_file} red icon="delete" label="Delete" style="align-self: flex-start;"/>
<Button click={delete_file} red icon="delete" label="Move to trash" style="align-self: flex-start;"/>
</fieldset>
{/if}

View File

@@ -73,7 +73,7 @@ run(() => {
<fieldset>
<legend>Embedding</legend>
<p>
If you have a website you can embed pixeldrain directories and files in your
If you have a website you can embed Nova directories and files in your
own webpages with the code below. If you embed a directory then all
subdirectories and files will be accessible through the frame. Branding
options will also apply in the frame, but only when applied to this

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { fs_delete_all, fs_rename } from "lib/FilesystemAPI.svelte"
import { fs_rename, fs_trash, FSNode } from "lib/FilesystemAPI.svelte"
import { onMount } from "svelte"
import CreateDirectory from "./CreateDirectory.svelte"
import ListView from "./ListView.svelte"
@@ -15,6 +15,7 @@ import { FileAction, type FileActionHandler } from "./FileManagerLib";
import FileMenu from "./FileMenu.svelte";
import { loading_finish, loading_start } from "lib/Loading";
import SearchBar from "../SearchBar.svelte";
import type { GenericResponse } from "lib/NovaAPI";
let {
nav = $bindable(),
@@ -33,11 +34,11 @@ let {
} = $props();
let large_icons = $state(false)
let uploader: FsUploadWidget
let uploader!: FsUploadWidget
let mode = $state("viewing")
let creating_dir = $state(false)
let show_hidden = $state(false)
let file_menu: FileMenu = $state()
let file_menu!: FileMenu
export const upload = (files: File[]) => {
return uploader.upload_files(files)
@@ -119,12 +120,12 @@ const delete_selected = async () => {
loading_start()
// Save all promises with deletion requests in an array
let promises = []
let promises: Promise<GenericResponse>[] = []
nav.children.forEach(child => {
if (!child.fm_selected) {
return
}
promises.push(fs_delete_all(child.path))
promises.push(fs_trash(child.path))
})
// Wait for all the promises to finish
@@ -175,7 +176,7 @@ const toggle_large_icons = () => {
// Moving functions
let moving_items = []
let moving_items: FSNode[] = []
// We need to detect if shift is pressed so we can select multiple items
let shift_pressed = false
@@ -320,6 +321,14 @@ onMount(() => {
<button onclick={() => nav.reload()} title="Refresh directory listing">
<i class="icon">refresh</i>
</button>
{#if $nav.children.some(c => c.name === ".Trash" && c.type === "dir")}
<button
onclick={() => nav.navigate($nav.children.find(c => c.name === ".Trash").path, true)}
title="Open trash bin"
>
<i class="icon">delete</i>
</button>
{/if}
</div>
<div class="toolbar_middle">

View File

@@ -4,7 +4,7 @@ import type { FSNavigator } from "filesystem/FSNavigator";
import Button from "layout/Button.svelte";
import Dialog from "layout/Dialog.svelte";
import { bookmark_add, bookmark_del, bookmarks_store, is_bookmark } from "lib/Bookmarks";
import { fs_delete, type FSNode } from "lib/FilesystemAPI.svelte";
import { fs_trash, type FSNode } from "lib/FilesystemAPI.svelte";
import { loading_finish, loading_start } from "lib/Loading";
import { tick } from "svelte";
@@ -44,7 +44,7 @@ export const open = async (n: FSNode, target: EventTarget, event: Event) => {
const delete_node = async () => {
try {
loading_start()
await fs_delete(node.path)
await fs_trash(node.path)
nav.reload()
} catch (err) {
alert(JSON.stringify(err))

View File

@@ -26,8 +26,16 @@ let {
<thead>
<tr>
<td></td>
<td><SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="name">Name</SortButton></td>
<td class="hide_small"><SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="file_size">Size</SortButton></td>
<td>
<SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="name">
Name
</SortButton>
</td>
<td class="hide_small">
<SortButton active_field={$nav.sort_last_field} asc={$nav.sort_asc} sort_func={nav.sort_children} field="file_size">
Size
</SortButton>
</td>
<td></td>
</tr>
</thead>

View File

@@ -1,4 +1,4 @@
// Uploads a file to the logged in user's pixeldrain account. If no user is
// Uploads a file to the logged in user's Nova account. If no user is
// logged in the file is uploaded anonymously.
//
// on_progress reports progress on the file upload, parameter 1 is the uploaded

View File

@@ -32,7 +32,7 @@ export const update = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: nav.base.name,
artist: "pixeldrain",
artist: "Nova",
album: "unknown",
});
}

View File

@@ -126,7 +126,7 @@ let magnet = $state("")
instructions for your torrent client to download the files from
other people who happen to be downloading the same files currently.
This means that instead of connecting to a single server (like
pixeldrain), you will be connecting to other people on the internet
Nova), you will be connecting to other people on the internet
to download these files.
</p>
<p>

View File

@@ -24,7 +24,7 @@ export const update = async () => {
if (media_session) {
navigator.mediaSession.metadata = new MediaMetadata({
title: node.name,
artist: "pixeldrain",
artist: "Nova",
album: "unknown",
});
console.debug("Updating media session")

View File

@@ -28,7 +28,7 @@ onMount(async () => {
{/snippet}
<p>
Your user account has been banned from uploading to
pixeldrain due to violation of the
Nova due to violation of the
<a href="/abuse">content policy</a>. Below is a list of
files originating from your account which have been blocked:
</p>
@@ -63,7 +63,7 @@ onMount(async () => {
</div>
<p>
If you would like to dispute your account ban you can mail me at
support@pixeldrain.com. Please do not mail unless you have a
support@nova.storage. Please do not mail unless you have a
good reason. If you do not provide a valid reason why the ban
should be reversed your e-mail will be ignored. And do not
forget to put your username ({window.user.username}) in the
@@ -87,7 +87,7 @@ onMount(async () => {
{#if result.ip_banned}
<p>
Your IP address ({result.address}) has been banned from
uploading to pixeldrain due to violation of the
uploading to Nova due to violation of the
<a href="/abuse">content policy</a>. Below is a list of
files originating from your IP address which have been
blocked:
@@ -96,7 +96,7 @@ onMount(async () => {
<p>
Your IP address ({result.address}) has received copyright
strikes. At 10 copyright strikes your IP address will be banned
and you will be unable to upload files to pixeldrain. Below is a
and you will be unable to upload files to Nova. Below is a
list of files originating from your IP address which have been
blocked:
</p>
@@ -151,7 +151,7 @@ onMount(async () => {
</p>
<p>
If you would like to dispute your IP ban you can mail me at
support@pixeldrain.com. Please do not mail unless you have a good
support@nova.storage. Please do not mail unless you have a good
reason. If you do not provide a valid reason why the IP ban should
be reversed your e-mail will be ignored. And do not forget to put
your IP address ({result.address}) in the e-mail.

View File

@@ -6,7 +6,7 @@ import OtherPlans from "./OtherPlans.svelte";
<section>
<p>
Pixeldrain features two different payment modes. We offer a monthly
Nova features two different payment modes. We offer a monthly
subscription which is managed by Patreon, and a Prepaid subscription
which supports a dozen different payment providers. With Prepaid you pay
in advance to charge your credit, then usage will be subtracted from
@@ -44,7 +44,7 @@ import OtherPlans from "./OtherPlans.svelte";
{/snippet}
The Pro subscription is managed by Patreon. Patreon's own fees
and sales tax will be added to this price. After paying you need
to link your pixeldrain account to Patreon to activate the plan.
to link your Nova account to Patreon to activate the plan.
</Tooltip>
</div>
<div class="feature_cell prepaid_feat">
@@ -224,13 +224,13 @@ import OtherPlans from "./OtherPlans.svelte";
<div class="bottom_row pro_feat">
{#if window.user.subscription.id === "patreon_1"}
You have this plan<br/>
Thank you for supporting pixeldrain!
Thank you for supporting Nova!
{:else}
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5736701&cadence=1" class="button button_highlight round">
<a href="https://www.patreon.com/join/nova/checkout?rid=5736701&cadence=1" class="button button_highlight round">
€ 4 per month
</a>
or
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5736701&cadence=12" class="button button_highlight round">
<a href="https://www.patreon.com/join/nova/checkout?rid=5736701&cadence=12" class="button button_highlight round">
€ 40 per year!
</a>
<br/>

View File

@@ -17,6 +17,11 @@ let upload_widget
<AddressReputation/>
<div class="page_content">
<div class="highlight_yellow">
This is a development server. All files and accounts may be lost when
the final product launches.
</div>
<section>
<p>
Nova.storage is a platform for cost-effective cloud storage and

View File

@@ -4,9 +4,9 @@
Persistence<br/>
{#if window.user.subscription.id === "patreon_2"}
You have this plan<br/>
Thank you for supporting pixeldrain!
Thank you for supporting Nova!
{:else}
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291482" class="button button_highlight round">
<a href="https://www.patreon.com/join/nova/checkout?rid=5291482" class="button button_highlight round">
€ 8
</a>
/ month
@@ -30,9 +30,9 @@
Tenacity<br/>
{#if window.user.subscription.id === "patreon_3"}
You have this plan<br/>
Thank you for supporting pixeldrain!
Thank you for supporting Nova!
{:else}
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291516" class="button button_highlight round">
<a href="https://www.patreon.com/join/nova/checkout?rid=5291516" class="button button_highlight round">
€ 16
</a>
/ month
@@ -51,9 +51,9 @@
Eternity<br/>
{#if window.user.subscription.id === "patreon_4"}
You have this plan<br/>
Thank you for supporting pixeldrain!
Thank you for supporting Nova!
{:else}
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=5291528" class="button button_highlight round">
<a href="https://www.patreon.com/join/nova/checkout?rid=5291528" class="button button_highlight round">
€ 32
</a>
/ month
@@ -72,9 +72,9 @@
Infinity<br/>
{#if window.user.subscription.id === "patreon_6"}
You have this plan<br/>
Thank you for supporting pixeldrain!
Thank you for supporting Nova!
{:else}
<a href="https://www.patreon.com/join/pixeldrain/checkout?rid=6573749" class="button button_highlight round">
<a href="https://www.patreon.com/join/nova/checkout?rid=6573749" class="button button_highlight round">
€ 64
</a>
/ month

View File

@@ -2,9 +2,9 @@
import Euro from "util/Euro.svelte";
import ProgressBar from "util/ProgressBar.svelte";
let fnx_storage = $state(0)
let fnx_egress = $state(0)
let fnx_total = $state(0)
let nova_storage = $state(0)
let nova_egress = $state(0)
let nova_total = $state(0)
let backblaze_storage = $state(0)
let backblaze_egress = $state(0)
let backblaze_api = $state(0)
@@ -20,9 +20,9 @@ let avg_file_size = $state(1000) // kB
$effect(() => {
// Nova has a minimum file size of 10kB. Calculate the number of files from
// storage and avg file size, then calculate storage usage based on that.
fnx_storage = Math.max(storage * 4, ((storage*10)/avg_file_size)*4)
fnx_egress = egress * 1
fnx_total = fnx_storage + fnx_egress
nova_storage = Math.max(storage * 4, ((storage*10)/avg_file_size)*4)
nova_egress = egress * 1
nova_total = nova_storage + nova_egress
// Egress at Backblaze is free up to three times the amount of storage, then
// it's $10/TB
@@ -35,7 +35,7 @@ $effect(() => {
wasabi_storage = storage * 6.99
wasabi_total = (egress > storage) ? 0 : wasabi_storage
price_max = Math.max(fnx_total, backblaze_total, wasabi_total)
price_max = Math.max(nova_total, backblaze_total, wasabi_total)
});
</script>
@@ -67,10 +67,10 @@ $effect(() => {
<div class="bars">
<div>
<div>
Nova.storage - <Euro amount={fnx_total*1e6}/> / month<br/>
<Euro amount={fnx_storage*1e6}/> storage,
<Euro amount={fnx_egress*1e6}/> egress
<ProgressBar used={fnx_total} total={price_max}/>
Nova.storage - <Euro amount={nova_total*1e6}/> / month<br/>
<Euro amount={nova_storage*1e6}/> storage,
<Euro amount={nova_egress*1e6}/> egress
<ProgressBar used={nova_total} total={price_max}/>
</div>
{#if avg_file_size < 10}
<div>

View File

@@ -3,10 +3,8 @@ import { onMount } from "svelte";
import Discord from "icons/Discord.svelte";
import Github from "icons/Github.svelte";
import Mastodon from "icons/Mastodon.svelte";
import Patreon from "icons/Patreon.svelte";
import Reddit from "icons/Reddit.svelte";
import { formatDataVolumeBits } from "util/Formatting";
import { get_endpoint, get_hostname } from "lib/PixeldrainAPI";
import { get_endpoint, get_hostname } from "lib/NovaAPI";
let { nobg = false }: {
nobg?: boolean;
@@ -34,20 +32,14 @@ onMount(async () => {
<footer class:nobg>
<div class="footer_content">
<div style="display: inline-block; margin: 0 8px;">
Pixeldrain is a product by
Nova is a product by
<a href="//fornaxian.tech" target="_blank" rel="noreferrer">Fornaxian Technologies</a>
</div>
<br/>
<div style="display: inline-block; margin: 0 8px;">
<a href="https://www.patreon.com/pixeldrain" target="_blank" rel="noreferrer">
<Patreon style="color: var(--body_text_color);"/> Patreon
</a> |
<a href="https://discord.gg/TWKGvYAFvX" target="_blank" rel="noreferrer">
<Discord style="color: var(--body_text_color);"/> Discord
</a> |
<a href="https://reddit.com/r/pixeldrain" target="_blank" rel="noreferrer">
<Reddit style="color: var(--body_text_color);"/> Reddit
</a> |
<a href="https://github.com/Fornaxian" target="_blank" rel="noreferrer">
<Github style="color: var(--body_text_color);"/> GitHub
</a> |

View File

@@ -4,7 +4,7 @@ import PickAmount from "./PickAmount.svelte";
import PickCountry from "./PickCountry.svelte";
import { payment_providers, type CheckoutState } from "./CheckoutLib";
import PickName from "./PickName.svelte";
import { get_misc_vat_rate, get_user } from "lib/PixeldrainAPI";
import { get_misc_vat_rate, get_user } from "lib/NovaAPI";
import { countries } from "country-data-list";
import PickProvider from "./PickProvider.svelte";

View File

@@ -1,5 +1,5 @@
import type { countries } from "country-data-list";
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
export type CheckoutState = {
country: typeof countries.all[0]

View File

@@ -64,7 +64,7 @@ const submit = async (e: SubmitEvent) => {
</form>
<hr/>
<p style="text-align: initial;">
This Pixeldrain premium plan costs €1 per month, but due to
This Nova premium plan costs €1 per month, but due to
processing fees we can't accept payments less than €10. So your
deposit will give roughly 10 months of premium service depending on
usage. You can track your spending on the <a

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy';
import { countries } from "country-data-list";
import { get_misc_vat_rate, put_user } from "lib/PixeldrainAPI";
import { get_misc_vat_rate, put_user } from "lib/NovaAPI";
import { payment_providers, type CheckoutState } from "./CheckoutLib";
import { loading_finish, loading_start } from "lib/Loading";

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { put_user } from "lib/PixeldrainAPI";
import { put_user } from "lib/NovaAPI";
import { type CheckoutState } from "./CheckoutLib";
let { status = $bindable() }: {
@@ -35,11 +35,10 @@ const submit = async (e: SubmitEvent) => {
</form>
<hr/>
<p style="text-align: initial;">
This Pixeldrain premium plan costs €1 per month, but due to
processing fees we can't accept payments less than €10. So your
deposit will give roughly 10 months of premium service depending on
usage. You can track your spending on the <a
href="/user/prepaid/transactions">transactions page</a>.
This Nova premium plan costs €1 per month, but due to processing fees we
can't accept payments less than €10. So your deposit will give roughly 10
months of premium service depending on usage. You can track your spending on
the <a href="/user/prepaid/transactions">transactions page</a>.
</p>
<style>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { put_user } from "lib/PixeldrainAPI";
import { put_user } from "lib/NovaAPI";
import { payment_providers, type CheckoutState, type PaymentProvider } from "./CheckoutLib";
let { status = $bindable() }: {

View File

@@ -1,5 +1,5 @@
import { countries } from "country-data-list"
import { check_response, get_endpoint } from "./PixeldrainAPI"
import { check_response, get_endpoint } from "./NovaAPI"
export const country_name = (country: string) => {
if (country !== "" && countries[country] !== undefined) {

View File

@@ -1,9 +1,9 @@
import { writable } from "svelte/store"
import { fs_check_response, fs_path_url, type FSNode } from "./FilesystemAPI.svelte"
import { loading_finish, loading_start } from "lib/Loading"
import { get_user } from "lib/PixeldrainAPI"
import { get_user } from "lib/NovaAPI"
const bookmarks_file = "/me/.fnx/bookmarks.json"
const bookmarks_file = "/me/.nova/bookmarks.json"
export type Bookmark = {
id: string,

View File

@@ -230,6 +230,29 @@ export const fs_delete_all = async (path: string) => {
) as GenericResponse
}
export const fs_trash = async (path: string) => {
return await fs_check_response(
await fetch(fs_path_url(path) + "?trash", { method: "DELETE" })
) as GenericResponse
}
export type TrashEntry = {
node: FSNode;
original_path: string;
deletion_date: string;
};
export const fs_get_trash = async (path: string) => {
let resp = await fs_check_response(
await fetch(fs_path_url(path) + "?trash")
) as TrashEntry[]
resp.forEach((entry, index) => {
resp[index].node = Object.assign(new FSNode(), entry.node)
})
return resp
};
export const fs_search = async (path: string, term: string, limit = 10) => {
return await fs_check_response(
await fetch(

View File

@@ -1,5 +1,5 @@
import { writable } from "svelte/store";
import { get_user, type Subscription, type User } from "./PixeldrainAPI";
import { get_user, type Subscription, type User } from "./NovaAPI";
export const user = writable(
{ subscription: {} as Subscription } as User,

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { createEventDispatcher, onMount } from "svelte";
import Form, { type FormConfig } from "util/Form.svelte"
import { check_response, get_endpoint } from "lib/PixeldrainAPI";
import { check_response, get_endpoint } from "lib/NovaAPI";
let dispatch = createEventDispatcher()

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import Form, { type FormConfig } from "util/Form.svelte"
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
let form: FormConfig = {
fields: [

View File

@@ -3,7 +3,7 @@ import TabMenu from "util/TabMenu.svelte";
import Register from "./Register.svelte";
import Login from "./Login.svelte";
import { onMount } from "svelte";
import { get_user } from "lib/PixeldrainAPI";
import { get_user } from "lib/NovaAPI";
let pages = [
{

View File

@@ -69,7 +69,7 @@ const reload_sheet = () => {
<div id="page_content" class="page_content">
<section>
<p>
You can change how pixeldrain looks! Your theme choice will
You can change how Nova looks! Your theme choice will
be saved in a cookie.
</p>
<h2>Theme</h2>
@@ -100,9 +100,9 @@ const reload_sheet = () => {
Classic 2022 style, with purple gradients
<br/>
<br/>
<input type="radio" id="style_classic" name="style"><label for="style_classic">Pixeldrain classic (gray)</label>
<input type="radio" id="style_classic" name="style"><label for="style_classic">Nova classic (gray)</label>
<br/>
Classic pre-2020 pixeldrain style, dark gray
Classic pre-2020 Nova style, dark gray
<br/>
<br/>
Other (experimental) themes
@@ -126,9 +126,9 @@ const reload_sheet = () => {
<br/>
<input type="radio" id="style_adwaita_light" name="style"><label for="style_adwaita_light">Adwaita light</label>
<br/><br/>
<input type="radio" id="style_pixeldrain98" name="style"><label for="style_pixeldrain98">Pixeldrain 98</label>
<input type="radio" id="style_nova98" name="style"><label for="style_nova98">Nova 98</label>
<br/>
<input type="radio" id="style_pixeldrain98_dark" name="style"><label for="style_pixeldrain98_dark">Pixeldrain 98 (dark)</label>
<input type="radio" id="style_nova98_dark" name="style"><label for="style_nova98_dark">Nova 98 (dark)</label>
<h2>Hue</h2>
<p>

View File

@@ -9,7 +9,7 @@ import Speedtest from "./Speedtest.svelte";
<section>
<h2>How does this work?</h2>
<p>
The speedtest measures the maximum download speed from pixeldrain's
The speedtest measures the maximum download speed from Nova's
servers to your computer. This speed is not affected by the daily
download limit for free users.
</p>
@@ -45,8 +45,8 @@ import Speedtest from "./Speedtest.svelte";
byte.
</p>
<p>
The third number shows the latency to the pixeldrain servers. This
is dependent on how far you are removed from the closest pixeldrain
The third number shows the latency to the Nova servers. This
is dependent on how far you are removed from the closest Nova
server. The lower the latency the faster your downloads will be,
generally. The number shows request latency and not ping latency.
HTTP requests have some overhead which means this latency number
@@ -65,7 +65,7 @@ import Speedtest from "./Speedtest.svelte";
because it stays within their network.
</p>
<p>
Pixeldrain does not have this luxury. Because our budget is very
Nova does not have this luxury. Because our budget is very
small we are only able to afford the cheapest bandwidth available.
This means that the data has to travel further and is more likely to
be throttled.
@@ -85,11 +85,11 @@ import Speedtest from "./Speedtest.svelte";
</p>
<ol>
<li>
Your ISP is limiting the connection speed to pixeldrain's
Your ISP is limiting the connection speed to Nova's
servers
</li>
<li>
The pixeldrain servers are overloaded
The Nova servers are overloaded
</li>
</ol>
<p>

View File

@@ -147,11 +147,11 @@ const logout = async (key) => {
<tr>
<td colspan="1">
{#if row.app_name === "website login"}
<img src="/res/img/pixeldrain_32.png" alt="Pixeldrain logo" class="app_icon"/>
Pixeldrain website
<img src="/res/img/nova_32.png" alt="Nova logo" class="app_icon"/>
Nova website
{:else if row.app_name === "website keys page"}
<i class="icon">vpn_key</i>
Pixeldrain keys page
Nova keys page
{:else if row.app_name === "sharex"}
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon"/>
ShareX

View File

@@ -4,7 +4,7 @@ import CopyButton from "layout/CopyButton.svelte";
import Form from "util/Form.svelte";
import Button from "layout/Button.svelte";
import OtpSetup from "./OTPSetup.svelte";
import { put_user } from "lib/PixeldrainAPI";
import { put_user } from "lib/NovaAPI";
let affiliate_link = window.location.protocol+"//"+window.location.host + "?ref=" + encodeURIComponent(window.user.username)
let affiliate_deny = $state(false)
@@ -114,7 +114,7 @@ const affiliate_settings = {
type: "text",
default_value: window.user.affiliate_user_name,
description: `The affiliate user name can be the name of a
pixeldrain account you wish to support with your subscription.
Nova account you wish to support with your subscription.
The account will receive a fee of €0.50 for every month that
your premium plan is active. This does not cost you anything
extra.`,
@@ -142,7 +142,7 @@ let delete_account = {
type: "description",
description: `
<p>
When you delete your pixeldrain account you will be
When you delete your Nova account you will be
logged out on all of your devices. Your account will be
scheduled for deletion in seven days. If you log back in to your
account during those seven days the deletion will be canceled.
@@ -161,7 +161,7 @@ let delete_account = {
<p>
If you have an active Pro subscription you need to end that
separately through your Patreon account. Deleting your
pixeldrain account will not cancel the subscription.
Nova account will not cancel the subscription.
</p>`,
},
],
@@ -200,14 +200,14 @@ let delete_account = {
Your own affiliate link is
<a href="{affiliate_link}">{affiliate_link}</a>
<CopyButton small_icon text={affiliate_link}/>. Share this link
with premium pixeldrain users to gain commissions. You can use
with premium Nova users to gain commissions. You can use
the "?ref={encodeURIComponent(window.user.username)}" referral
code on download pages too. For a detailed description of the
affiliate program please check out the <a
href="/about#toc_12">Q&A page</a>.
</p>
<p>
Note that the link includes the name of your pixeldrain
Note that the link includes the name of your Nova
account. If you change your account name the link will stop
working and you might stop receiving commissions.
</p>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import Modal from "util/Modal.svelte";
import { get_user, put_user } from "lib/PixeldrainAPI";
import { get_user, put_user } from "lib/NovaAPI";
import { loading_finish, loading_start } from "lib/Loading";
let { always = false }: {
@@ -74,8 +74,8 @@ const deny = () => {
<Modal bind:this={modal} title="Affiliate sponsoring request" width="700px">
<section>
<p>
Hi! {referral} wants you to sponsor their pixeldrain account. This
will give them €0.50 every month in pixeldrain prepaid credit. They
Hi! {referral} wants you to sponsor their Nova account. This
will give them €0.50 every month in Nova prepaid credit. They
can use this credit to get a discount on their file storage and
sharing costs. Here is a short summary of what this entails:
</p>
@@ -86,12 +86,12 @@ const deny = () => {
longer receive commissions.
</li>
<li>
Pixeldrain credit cannot be cashed out. So they are not earning
Nova credit cannot be cashed out. So they are not earning
real money with this.
</li>
<li>
This does not cost you any extra money. The commissions paid out
to the creator are paid for by pixeldrain itself.
to the creator are paid for by Nova itself.
</li>
<li>
You can change who you are sponsoring at any time on your <a

View File

@@ -134,7 +134,7 @@ onMount(() => {
<p>
This setting only applies to your account, others will still see the
download page when visiting your files. When this is enabled you will be
redirected to the file download API when clicking a pixeldrain link.
redirected to the file download API when clicking a Nova link.
This means that images, videos and PDF files will be opened directly in
your browser, and other files are immediately downloaded to your device.
This only works for single file links, not albums. You will need a Pro

View File

@@ -60,13 +60,13 @@ onMount(() => {
<h2>
Connect
<img src="/res/img/jdownloader.png" alt="JDownloader logo" class="app_icon_small"/>
JDownloader to your pixeldrain account
JDownloader to your Nova account
</h2>
<p>
JDownloader is a software program which makes it easier to download
things from the web. You can connect JDownloader to your pixeldrain
account to benefit from faster download speed and other pixeldrain
Pro features.
things from the web. You can connect JDownloader to your Nova
account to benefit from faster download speed and other Nova Pro
features.
</p>
<h3>Step 1: Install JDownloader</h3>
<p>
@@ -81,11 +81,11 @@ onMount(() => {
</div>
<h3>Step 3: Generate an app key</h3>
<p>
To connect JDownloader to pixeldrain you need to generate an API
To connect JDownloader to Nova you need to generate an API
key and enter it in JDownloader's Account Manager.
<br/>
<strong>Do not show the generated key to anyone</strong>, it can
be used to gain access to your pixeldrain account!
be used to gain access to your Nova account!
</p>
{#if !api_key}
@@ -121,46 +121,46 @@ onMount(() => {
</div>
<p>
Click Save and you're done! You can now download files from
pixeldrain with JDownloader.
Nova with JDownloader.
</p>
{:else if app_name === "sharex"}
<h2>
Connect
<img src="/res/img/sharex.png" alt="ShareX logo" class="app_icon_small"/>
ShareX to your pixeldrain account
ShareX to your Nova account
</h2>
<p>
ShareX is a Screen capture, file sharing and productivity tool.
Pixeldrain is supported as a custom uploader. You can <a
Nova is supported as a custom uploader. You can <a
href="https://getsharex.com/" target="_blank" rel="noreferrer">get
ShareX here</a>.
</p>
<p>
Here you can download our custom ShareX uploader which uses
pixeldrain to upload your files. This uploader is configured to
upload files to your personal pixeldrain account. <strong>Do not
Nova to upload your files. This uploader is configured to
upload files to your personal Nova account. <strong>Do not
share the configuration file with anyone</strong>, it contains
your account credentials.
</p>
<div class="center">
<a href="/misc/sharex/pixeldrain.com.sxcu" class="button button_highlight">
<a href="/misc/sharex/nova.storage.sxcu" class="button button_highlight">
<i class="icon small">save</i>
Download ShareX Uploader
</a>
</div>
<h3>Setting pixeldrain as default uploader</h3>
<h3>Setting Nova as default uploader</h3>
<p>
Download the uploader config and choose 'Open file'
<br/>
<img src="/res/img/sharex_download.png" style="max-width: 100%;" alt=""/><br/>
Set pixeldrain.com as active uploader. Choose Yes
Set nova.storage as active uploader. Choose Yes
<br/>
<img src="/res/img/sharex_default.png" style="max-width: 100%;" alt=""/><br/>
</p>
{:else}
<h2>Connect an app to your pixeldrain account</h2>
<h2>Connect an app to your Nova account</h2>
<ul>
<li>
<a href="?app=jdownloader" class="button">

View File

@@ -76,7 +76,7 @@ onMount(() => {
<section>
<h2 id="deposit">Deposit credit</h2>
<p>
Pixeldrain credit can be used for our Prepaid subscription plan, which
Nova credit can be used for our Prepaid subscription plan, which
is different from the Patreon plans. Instead of monthly limits, with
Prepaid there are no limits. You pay for what you use, at a rate of €4
per TB per month for storage and €1 per TB for data transfer. Your

View File

@@ -61,7 +61,7 @@ onMount(() => {
</p>
<p>
The list should be formatted as a list of domain names separated by a
space. Like this: 'pixeldrain.com google.com twitter.com'
space. Like this: 'nova.storage google.com twitter.com'
</p>
Domain names:<br/>
<form class="form_row" onsubmit={preventDefault(save_embed)}>

View File

@@ -30,7 +30,7 @@ let frac = $derived(used / total)
<p>
You have used all of your data cap. People can still download your
files, but premium features are disabled. This means that the
download page shows pixeldrain branding, people who download your
download page shows Nova branding, people who download your
files have a daily download limit and hotlinking is disabled.
</p>
@@ -51,7 +51,7 @@ let frac = $derived(used / total)
<p>
You have used {(frac*100).toFixed(0)}% of your data cap. If your
data runs out the premium features related to downloading will be
disabled. This means that the download page shows pixeldrain
disabled. This means that the download page shows Nova
branding, people who download your files have a daily download limit
and hotlinking is disabled.
</p>

View File

@@ -3,7 +3,7 @@ import { onMount } from "svelte";
import Button from "layout/Button.svelte";
import CopyButton from "layout/CopyButton.svelte";
import ToggleButton from "layout/ToggleButton.svelte";
import { check_response, get_endpoint, get_user, type User } from "lib/PixeldrainAPI";
import { check_response, get_endpoint, get_user, type User } from "lib/NovaAPI";
let user: User = $state(null)
let secret = $state("")

View File

@@ -31,23 +31,23 @@ onMount(() => {
{#if patreon_result !== ""}
{#if patreon_error === "patreon_authentication_denied"}
<div class="highlight_yellow">
Please press "Allow" when asked if pixeldrain can access your
Please press "Allow" when asked if Nova can access your
profile.
</div>
{:else if patreon_result === "error"}
<div class="highlight_red">
<p>
An error occurred while linking Patreon subscription. Check if
there are any Pixeldrain integrations under "Logged in with
there are any Nova integrations under "Logged in with
Patreon" on this page: <a
href="https://www.patreon.com/settings/apps">https://www.patreon.com/settings/apps</a>.
Try disconnecting all Pixeldrain logins and try again.
Try disconnecting all Nova logins and try again.
</p>
<li>
This can also happen if you canceled your Patreon membership
before upgrading your pixeldrain account. In that case, go to
before upgrading your Nova account. In that case, go to
the <a
href="https://www.patreon.com/pixeldrain/membership">memberships
href="https://www.patreon.com/nova/membership">memberships
page</a> and activate your membership again. If you already paid
in the last 30 days you will not be charged again.
</li>
@@ -88,17 +88,17 @@ onMount(() => {
You pledged without selecting a support tier. It happens
sometimes that people pledge an amount of money without
selecting a support tier. If you don't have a tier
pixeldrain won't know which subscription you should get,
Nova won't know which subscription you should get,
regardless of how much you paid. To fix this you can select
a tier from <a
href="https://www.patreon.com/pixeldrain/membership">the
href="https://www.patreon.com/nova/membership">the
memberships page</a> and proceed to checkout. If you already
pledged the right amount you will not have to pay again.
</li>
<li>
Or you have not pledged at all yet. In that case it's
simple: <a
href="https://www.patreon.com/pixeldrain/membership">go to
href="https://www.patreon.com/nova/membership">go to
Patreon</a> and purchase a membership.
</li>
</ol>
@@ -110,7 +110,7 @@ onMount(() => {
</div>
{:else if patreon_result === "success"}
<div class="highlight_green">
Success! Your Patreon pledge has been linked to your pixeldrain
Success! Your Patreon pledge has been linked to your Nova
account. You are now able to use Pro features.
</div>
{/if}

View File

@@ -12,7 +12,7 @@ import EmbeddingControls from "./EmbeddingControls.svelte";
import Dashboard from "./dashboard/Dashboard.svelte";
import AffiliatePrompt from "./AffiliatePrompt.svelte";
import { onMount } from "svelte";
import { get_user, type User } from "lib/PixeldrainAPI";
import { get_user, type User } from "lib/NovaAPI";
let pages: Tab[] = [
{

View File

@@ -4,7 +4,7 @@ import SuccessMessage from "util/SuccessMessage.svelte";
import PatreonActivationResult from "./PatreonActivationResult.svelte";
import { loading_finish, loading_start } from "lib/Loading";
import { user } from "lib/UserStore";
import { put_user } from "lib/PixeldrainAPI";
import { put_user } from "lib/NovaAPI";
let subscription = $state($user.subscription.id)
let subscription_type = $state($user.subscription.type)
@@ -31,7 +31,7 @@ const update = async (plan: string) => {
<div class="highlight_green">
<h2>Payment successful!</h2>
<p>
Thank you for supporting pixeldrain! The credit has been added
Thank you for supporting Nova! The credit has been added
to your account balance.
</p>
<p>
@@ -42,7 +42,7 @@ const update = async (plan: string) => {
before your credit is deposited. SEPA transfers can take up to
two working days for example. When the deposit is complete you
will receive an e-mail. If it takes too long, contact
support@pixeldrain.com.
support@nova.storage.
</p>
</div>
{:else if window.location.hash === "#order_expired"}
@@ -72,10 +72,10 @@ const update = async (plan: string) => {
Here you can switch between different subscription plans.
</p>
<p>
The Patreon subscription is managed by Patreon. Pixeldrain cannot modify
The Patreon subscription is managed by Patreon. Nova cannot modify
or end your subsciption. If you would like to cancel your Patreon plan
you can do that <a
href="https://www.patreon.com/settings/memberships/pixeldrain"
href="https://www.patreon.com/settings/memberships/nova"
target="_blank">on Patreon</a>.
</p>
<p>
@@ -96,7 +96,7 @@ const update = async (plan: string) => {
Patreon<br/>
{#if subscription_type === "patreon"}
Currently active<br/>
<a class="button" href="https://www.patreon.com/settings/memberships/pixeldrain" target="_blank">
<a class="button" href="https://www.patreon.com/settings/memberships/nova" target="_blank">
<i class="icon">settings</i>
Manage
</a>
@@ -110,7 +110,7 @@ const update = async (plan: string) => {
<div class="feat_normal round_tr" class:feat_highlight={subscription_type === "patreon"}>
<p>
This subscription is managed by Patreon. You will need to <a
href="https://www.patreon.com/pixeldrain/membership"
href="https://www.patreon.com/nova/membership"
target="_blank">purchase a plan on Patreon</a> before you
can activate this subscription. After your purchase you can
click the "Link Patreon" button and your account will be
@@ -144,7 +144,7 @@ const update = async (plan: string) => {
plan. If you currently have a Patreon subscription active,
then enabling prepaid will not cancel that subscription. You
can end your subscription <a
href="https://www.patreon.com/settings/memberships/pixeldrain"
href="https://www.patreon.com/settings/memberships/nova"
target="_blank">on Patreon.com</a>.
</p>
<ul>

View File

@@ -103,7 +103,7 @@ onMount(() => {
of days in a month (30.4375).
</p>
<p>
Example: If you have 2 TB stored on your pixeldrain account at €4 per TB
Example: If you have 2 TB stored on your Nova account at €4 per TB
then the daily charge will be:<br/>
2 TB * € 4 = € 8<br/>

View File

@@ -1,6 +1,6 @@
<script>
import Button from "layout/Button.svelte";
import { logout_user } from "lib/PixeldrainAPI";
import { logout_user } from "lib/NovaAPI";
</script>
<ul>
<li>Username: {window.user.username}</li>

View File

@@ -3,7 +3,7 @@ import { onMount } from "svelte";
import Chart from "util/Chart.svelte";
import { color_by_name } from "util/Util";
import { formatDataVolume, formatThousands } from "util/Formatting";
import { get_endpoint } from "lib/PixeldrainAPI";
import { get_endpoint } from "lib/NovaAPI";
let { card_size = 1 }: {
card_size?: number;

View File

@@ -50,7 +50,7 @@ import { formatDataVolume } from "util/Formatting";
{#if window.user.subscription.id !== ""}
<li>
Support: For questions related to your account you can send a
message to support@pixeldrain.com
message to support@nova.storage
</li>
{/if}
</ul>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { get_endpoint, get_user, type User } from "lib/PixeldrainAPI";
import { get_endpoint, get_user, type User } from "lib/NovaAPI";
import { onMount } from "svelte";
import HotlinkProgressBar from "user_home/HotlinkProgressBar.svelte";
import StorageProgressBar from "user_home/StorageProgressBar.svelte";

View File

@@ -26,7 +26,7 @@ export type SubmitResult = {
<script lang="ts">
import { onMount } from "svelte";
import Spinner from "./Spinner.svelte";
import type { GenericResponse } from "lib/PixeldrainAPI";
import type { GenericResponse } from "lib/NovaAPI";
let { config }: {
config: FormConfig;

View File

@@ -13,7 +13,7 @@ import { fs_get_node } from "lib/FilesystemAPI.svelte";
import { css_from_path } from "filesystem/edit_window/Branding";
import { loading_run, loading_store } from "lib/Loading";
import Spinner from "util/Spinner.svelte";
import { get_user } from "lib/PixeldrainAPI";
import { get_user } from "lib/NovaAPI";
import Tree from "./Tree.svelte";
import MenuEntry from "./MenuEntry.svelte";
import { writable } from "svelte/store";
@@ -206,6 +206,10 @@ const set_offset = (off: number) => {
<i class="icon">folder</i>
<span>Filesystem</span>
</a>
<a class="button" href="/d/me/.Trash" use:highlight_current_page>
<i class="icon">delete</i>
<span>Trash</span>
</a>
<a class="button" href="/user" use:highlight_current_page>
<i class="icon">person</i>
<span>Account</span>

View File

@@ -10,7 +10,7 @@ import SpeedtestPage from "speedtest/SpeedtestPage.svelte";
import Appearance from "pages/Appearance.svelte";
import Footer from "layout/Footer.svelte";
import { current_page_store, type Tab } from "./RouterStore";
import { get_user, type User } from "lib/PixeldrainAPI";
import { get_user, type User } from "lib/NovaAPI";
import { breadcrumbs_store } from "./BreadcrumbStore";
let pages: Tab[] = [

View File

@@ -24,7 +24,7 @@ export default defineConfig({
sourcemap: !production,
lib: {
entry: "src/wrap.js",
name: "fnx_web",
name: "nova_web",
fileName: name,
}
},

12
web.go
View File

@@ -9,15 +9,15 @@ import (
"github.com/julienschmidt/httprouter"
)
// DefaultConfig is the default configuration for Pixeldrain Web
const DefaultConfig = `## Pixeldrain Web UI server configuration
// DefaultConfig is the default configuration for Nova Web
const DefaultConfig = `## Nova Web UI server configuration
# Address used in the browser for making requests directly to the API. Can be
# relative to the current domain name
api_url_external = "/api"
# Address used to make internal API requests to the backend
api_url_internal = "https://pixeldrain.com/api"
api_url_internal = "https://nova.storage/api"
# When connecting to the API over a Unix domain socket you should enter the
# socket path here. api_url_internal needs to be correct too, as the API path
@@ -38,7 +38,7 @@ proxy_api_requests = true
maintenance_mode = false
`
// Init initializes the Pixeldrain Web UI controllers
// Init initializes the Nova Web UI controllers
func Init(r *httprouter.Router, prefix string, setLogLevel bool) {
log.Colours = true
log.Info("Starting web UI server (PID %v)", os.Getpid())
@@ -46,8 +46,8 @@ func Init(r *httprouter.Router, prefix string, setLogLevel bool) {
var conf webcontroller.Config
var _, err = config.New(
DefaultConfig,
"/etc/fnx",
"fnx_web.toml",
"/etc/nova",
"nova_web.toml",
&conf,
true,
)

View File

@@ -47,7 +47,7 @@ func (wc *WebController) serveDirectory(w http.ResponseWriter, r *http.Request,
return
}
td.Title = fmt.Sprintf("%s ~ pixeldrain", node.Path[node.BaseIndex].Name)
td.Title = fmt.Sprintf("%s ~ Nova", node.Path[node.BaseIndex].Name)
td.Other = node
td.OGData = wc.metadataFromFilesystem(r, node)
err = wc.templates.Run(w, r, "wrap", td)

View File

@@ -82,19 +82,19 @@ func userStyle(style string, hue int) template.CSS {
def = hackerStyle
hue = -1 // Does not support custom hues
case "canta":
def = cantaPixeldrainStyle
def = cantaNovaStyle
case "skeuos":
def = skeuosPixeldrainStyle
def = skeuosNovaStyle
case "sweet":
def = sweetPixeldrainStyle
def = sweetNovaStyle
case "adwaita_dark":
def = adwaitaDarkStyle
case "adwaita_light":
def = adwaitaLightStyle
case "pixeldrain98":
def = pixeldrain98Style
case "pixeldrain98_dark":
def = pixeldrain98StyleDark
case "nova98":
def = nova98Style
case "nova98_dark":
def = nova98StyleDark
}
if hue >= 0 && hue <= 360 {
@@ -370,7 +370,7 @@ var hackerStyle = styleSheet{
CardColor: HSL{120, .4, .05},
}
var cantaPixeldrainStyle = styleSheet{
var cantaNovaStyle = styleSheet{
Input: HSL{167, .06, .30}, // hsl(167, 6%, 30%)
InputHover: HSL{167, .06, .35}, // hsl(167, 6%, 30%)
InputText: HSL{0, 0, 1},
@@ -385,7 +385,7 @@ var cantaPixeldrainStyle = styleSheet{
CardColor: HSL{170, .05, .26},
}
var skeuosPixeldrainStyle = styleSheet{
var skeuosNovaStyle = styleSheet{
Input: HSL{226, .15, .23}, //hsl(226, 15%, 23%)
InputHover: HSL{226, .15, .28},
InputText: HSL{60, .06, .93},
@@ -452,7 +452,7 @@ var nordLightStyle = styleSheet{
CardColor: nord5,
}
var sweetPixeldrainStyle = styleSheet{
var sweetNovaStyle = styleSheet{
Input: HSL{229, .25, .18}, // hsl(229, 25%, 14%)
InputHover: HSL{229, .25, .23}, // hsl(229, 25%, 14%)
InputText: HSL{223, .13, .79},
@@ -610,7 +610,7 @@ hr,
}
`
var pixeldrain98Style = styleSheet{
var nova98Style = styleSheet{
Input: RGB{190, 190, 190},
InputHover: RGB{220, 220, 220},
InputText: RGB{0, 0, 0},
@@ -629,7 +629,7 @@ var pixeldrain98Style = styleSheet{
StyleOverrides: override98,
}
var pixeldrain98StyleDark = styleSheet{
var nova98StyleDark = styleSheet{
Input: HSL{0, 0, .25},
InputHover: HSL{0, 0, .30},
InputText: RGB{255, 255, 255},

View File

@@ -0,0 +1,54 @@
package webcontroller
import (
"net/http"
"os"
"fornaxian.tech/log"
"github.com/julienschmidt/httprouter"
)
// New initializes a new WebController by registering all the request handlers
// and parsing all templates in the resource directory
func serveSK(r *httprouter.Router) (err error) {
// r.ServeFiles("/_app/*filepath", http.Dir(assetsPath+"/_app"))
// r.ServeFiles("/style/*filepath", http.Dir(assetsPath+"/style"))
readdir, err := os.ReadDir(assetsPath)
if err != nil {
return err
}
for _, entry := range readdir {
if entry.Type().IsRegular() {
serveFile(r, "/"+entry.Name(), assetsPath+"/"+entry.Name())
} else if entry.IsDir() {
r.ServeFiles("/"+entry.Name()+"/*filepath", http.Dir(assetsPath+"/"+entry.Name()))
}
}
r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Debug("Running fallback handler for %s", r.URL.Path)
http.ServeFile(w, r, assetsPath+"/fallback.html")
})
return nil
}
const assetsPath = "/home/wim/Workspace/svelte/fnx_sk/build"
func serveFile(r *httprouter.Router, path, file string) {
var handler = func(
w http.ResponseWriter,
r *http.Request,
p httprouter.Params,
) {
http.ServeFile(w, r, assetsPath+"/"+file)
}
r.GET(path, handler)
r.HEAD(path, handler)
r.OPTIONS(path, handler)
r.POST(path, handler)
r.PUT(path, handler)
r.PATCH(path, handler)
r.DELETE(path, handler)
}

View File

@@ -79,7 +79,7 @@ func (wc *WebController) newTemplateData(w http.ResponseWriter, r *http.Request)
Value: "",
Path: "/",
Expires: time.Unix(0, 0),
Domain: ".pixeldrain.com",
Domain: ".nova.storage",
})
}
return t

View File

@@ -118,7 +118,6 @@ func New(r *httprouter.Router, prefix string, conf Config) (wc *WebController) {
}
r.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info("running wrap handler")
wc.serveTemplate("wrap", handlerOpts{})(w, r, nil)
})
@@ -134,15 +133,6 @@ func New(r *httprouter.Router, prefix string, conf Config) (wc *WebController) {
{GET, "api" /* */, wc.serveMarkdown("api.md", handlerOpts{})},
{GET, "d/*path" /* */, wc.serveDirectory},
{GET, "widgets" /* */, wc.serveTemplate("widgets", handlerOpts{})},
{GET, "about" /* */, wc.serveMarkdown("about.md", handlerOpts{})},
{GET, "hosting" /* */, wc.serveMarkdown("hosting.md", handlerOpts{})},
{GET, "acknowledgements" /**/, wc.serveMarkdown("acknowledgements.md", handlerOpts{})},
{GET, "business" /* */, wc.serveMarkdown("business.md", handlerOpts{})},
{GET, "limits" /* */, wc.serveMarkdown("limits.md", handlerOpts{})},
{GET, "abuse" /* */, wc.serveMarkdown("abuse.md", handlerOpts{})},
{GET, "filesystem" /* */, wc.serveMarkdown("filesystem.md", handlerOpts{})},
{GET, "100_gigabit_ethernet", wc.serveMarkdown("100_gigabit_ethernet.md", handlerOpts{NoExec: true})},
{GET, "apps" /* */, wc.serveTemplate("apps", handlerOpts{})},
{GET, "status" /* */, wc.serveTemplate("status", handlerOpts{})},
{GET, "theme.css", wc.themeHandler},
} {
@@ -287,5 +277,5 @@ func (wc *WebController) getAPIKey(r *http.Request) (key string, err error) {
return cookie.Value, nil
}
}
return "", errors.New("not a valid pixeldrain authentication cookie")
return "", errors.New("not a valid Nova authentication cookie")
}