diff --git a/ccvpn/views.py b/ccvpn/views.py index 7310c07..2f1e5c7 100644 --- a/ccvpn/views.py +++ b/ccvpn/views.py @@ -15,6 +15,7 @@ from django.utils.translation import ( ) from django.template.loader import TemplateDoesNotExist, get_template from django.template import Template +import django_lcore from constance import config import frontmatter @@ -22,11 +23,28 @@ from downloads.models import Version from .common import get_price_float +@django_lcore.APICache(initial=[]) +def get_gateway_locations(): + gateways = django_lcore.api.get("gateways/", enabled=True) + locations = set() + + for gw in gateways.list_iter(): + if not gw["tags"]: + continue + loc = gw["tags"].get("location") + if not loc: + continue + locations.add((loc["lat"], loc["lon"])) + + return list(locations) def index(request): - eur = '%.2f' % get_price_float() - return render(request, 'ccvpn/index.html', dict(eur_price=eur, motd=config.MOTD)) + return render(request, 'ccvpn/index.html', { + "eur_price": '%.2f' % get_price_float(), + "motd": config.MOTD, + "gateway_locations": get_gateway_locations(), + }) def chat(request): diff --git a/static/css/style.css b/static/css/style.css index 770c7c4..9f45d55 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -496,83 +496,107 @@ ul.errorlist { /***************************************************/ /********************* Home Page */ -.home-content { - width: 75%; - margin: 3em auto; +.home-signup { + text-align: center; } -.home-item{ - float: left; +a.home-signup-button { + padding: 0.75em 2em; +} + +@media screen and (max-width: 48em) { + .home-signup-button { + min-width: 50%; + } +} + + +.homepage h2 { + font-weight: 400; text-align: center; + font-family: 'Arvo', serif; + font-size: 1.6em; + margin: 3em; } -.home-item img{ - width: 60%; - margin: auto; + +.homepage .title { + color: #1c619a } -.home-item h2{ - color: #1c619a; - text-transform: uppercase; - font-weight: normal; - font-size: 1.5em; - margin: 0.80em 0 0.60em 0; +.homepage .title h3 { + width: 50%; + margin: auto; + border-bottom: 3px solid #1c619a; + border-radius: 5px; } -.home-item p{ - margin: 0px; - padding: 0.2em; + +.homepage .inner { + width: 50%; + margin: auto; } -.home-item b{ color: #1c619a; } -.home-item ul{ - margin: 0; - padding: 0 1.5em; + +.homepage .features { + margin: 2em 0; } -.home-item ul li{ - margin: 0px; - padding: 0px; - list-style: none; + +.homepage .right h3 { + text-align: right; } -.home-signup { - text-align: center; +.homepage h3 { + font-family: 'Arvo', serif; + font-weight: normal; + font-size: 1.2em; } -a.home-signup-button { - padding: 0.75em 2em; +.homepage ul { + list-style-type: circle; + margin: 1em; + padding-left: 1em; +} +.homepage p { + margin: 0.75em; } -.home-secondary > div { - padding: 0.5em 2em; +.homepage .home-signup { + margin: 3em 1em; } -.home-secondary b:first-child { + +.homepage .locations { + width: 70%; + margin: 4em auto; +} +.homepage .more { + text-align: right; +} +.homepage .map { display: block; } +#worldmap svg { + stroke-width: 0.5; +} -@media screen and (max-width: 80em) { - .home-content { - width: 100%; - } +#map { + display: block; + width: 100%; + margin: auto; } @media screen and (max-width: 48em) { - .home-content { - width: 100%; - margin-top: 0; + .homepage .features { + margin: 0; } - .home-item-content { - margin-left: 20%; + .homepage .title h3 { + margin-top: 2em; } - .home-item h2 { - margin: 1em 0 0.20em 0; - } - .home-item img { - width: 20%; - float: left; - margin-top: 3em; + .homepage .title h3, .homepage .inner { + width: 90%; } - .home-signup-button { - min-width: 50%; + .homepage .locations { + width: 100%; } } + /***************************************************/ /********************* Account */ @@ -1023,6 +1047,22 @@ div.ticket-message-private { unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } +@font-face { + font-family: 'Arvo'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('../fonts/arvo-0.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: 'Arvo'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('../fonts/arvo-1.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} /***************************************************/ diff --git a/static/fonts/arvo-0.woff2 b/static/fonts/arvo-0.woff2 new file mode 100644 index 0000000..cc286d4 Binary files /dev/null and b/static/fonts/arvo-0.woff2 differ diff --git a/static/fonts/arvo-1.woff2 b/static/fonts/arvo-1.woff2 new file mode 100644 index 0000000..5d0f882 Binary files /dev/null and b/static/fonts/arvo-1.woff2 differ diff --git a/static/img/world.svg b/static/img/world.svg new file mode 100644 index 0000000..b22a1eb --- /dev/null +++ b/static/img/world.svg @@ -0,0 +1,979 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/js/map.js b/static/js/map.js new file mode 100644 index 0000000..b74c4d0 --- /dev/null +++ b/static/js/map.js @@ -0,0 +1,104 @@ +(function() { + function createSvgEl(tag) { + return document.createElementNS("http://www.w3.org/2000/svg", tag); + } + + function lerp(datax, datay, x) { + for (let i=0; i x || hi < x) + continue; + const loy = datay[i]; + const hiy = datay[i + 1]; + return loy + (hiy-loy) * ((x - lo) / (hi - lo)); + } + } + + function robinson(lat, lon, radius, centerMeridian, adjX, adjY) { + const table = [ + // lat a b + [0, 1.0000, 0.0000], + [5, 0.9986, 0.0314], + [10, 0.9954, 0.0629], + [15, 0.9900, 0.0943], + [20, 0.9822, 0.1258], + [25, 0.9730, 0.1572], + [30, 0.9600, 0.1887], + [35, 0.9427, 0.2201], + [40, 0.9216, 0.2515], + [45, 0.8962, 0.2826], + [50, 0.8679, 0.3132], + [55, 0.8350, 0.3433], + [60, 0.7986, 0.3726], + [65, 0.7597, 0.4008], + [70, 0.7186, 0.4278], + [75, 0.6732, 0.4532], + [80, 0.6213, 0.4765], + [85, 0.5722, 0.4951], + [90, 0.5322, 0.5072], + ]; + const a = lerp(table.map(r => r[0]), table.map(r => r[1]), Math.abs(lat)); + const b = lerp(table.map(r => r[0]), table.map(r => r[2]), Math.abs(lat)); + const x = 0.8487 * radius * a * ((lon * Math.PI/180) - centerMeridian) * adjX; + const y = (1.3523 * radius * b * adjY) * (lat < 0 ? -1 : 1); + return [x, y]; + } + + function hexbyte(n) { + return Math.round(n).toString(16).padStart(2, '0') + } + + function ring(x, y, radius, strokeWid, trans) { + const circle = createSvgEl("circle"); + circle.setAttribute("cx", x); + circle.setAttribute("cy", y); + circle.setAttribute("r", radius); + circle.setAttribute("stroke", "#1c619a" + hexbyte(80 + trans * 5)); + circle.setAttribute("stroke-width", strokeWid); + circle.setAttribute("fill", "#1c619a" + hexbyte(trans)); + return circle; + } + + function drawServerLocation(e, data) { + if (!e || !data) return; + + const svg = e.contentDocument.querySelector("svg"); + + const height = svg.height.baseVal.value; + const width = svg.width.baseVal.value; + const earthRadius = width / 2.666269758 / 2; + const center = 0; + const adjX = 1.02; + const adjY = 2; + const offX = -12; + const offY = 75; + + for (point of data) { + const xy = robinson(point[0], point[1], earthRadius, center, adjX, adjY); + const realX = (width / 2) + xy[0] + offX; + const realY = (height / 2) - (xy[1]) + offY; + + const dot = createSvgEl("circle"); + dot.setAttribute("cx", realX); + dot.setAttribute("cy", realY); + dot.setAttribute("r", 5); + dot.setAttribute("fill", "#1c619a"); + svg.appendChild(dot); + + const rf = 5; + const rs = 15; + + svg.appendChild(ring(realX, realY, rf + rs * 1, 2, 20)); + svg.appendChild(ring(realX, realY, rf + rs * 2, 1, 15)); + svg.appendChild(ring(realX, realY, rf + rs * 3, 0.5, 10)); + } + } + + window.addEventListener("load", function() { + drawServerLocation( + document.getElementById("map"), + JSON.parse(document.getElementById('locations-data').textContent) + ); + }); +})(); \ No newline at end of file diff --git a/templates/ccvpn/index.html b/templates/ccvpn/index.html index 0b81bb0..83f7be4 100644 --- a/templates/ccvpn/index.html +++ b/templates/ccvpn/index.html @@ -3,95 +3,64 @@ {% load static %} {% block headers %} - + {% endblock %} {% block content %} -
-
-
- -
-

{% trans 'Unlimited' %}

-

{% trans 'Unlimited bandwidth' %}.
- {% trans 'Uncensored' %}.
- {% trans 'The data must flow.' %}

-
+
+

Secure your browsing with a VPN

+ +
+
+

Hide your IP Address

+
+
+

From corporations and attackers tracking you for advertising, marketing, geo-blocking, extorsion, ... + and irresponsibly storing personal data that can leak to malicious parties and be used against you.

-
- -
-

{% trans 'Affordable' %}

-

{{eur_price}}€ {% trans 'per month!' %}
- {% trans 'Pay by card, PayPal, or 40+ cryptocurrencies.' %}

-
+
+
+
+

through a Secure Tunnel to the Internet

-
- -
-

{% trans 'Secure' %}

-

{% trans 'Encrypted tunnel' %} - {% trans 'with an anonymous address.' %}
- {% trans 'Supports DNSSEC and PFS.' %}

-
+
+

+ ‣ Defend against eavesdropping & tampering, from compromised WiFi access points to intrusive corporate monitoring and Deep Packet Inspection.

+

+ ‣ Avoid network limitations (IPv6 unavailable, bad peering, misconfigured firewalls, ...) + and circumvent interception attempts (lying DNS, DNS blocking, ...) +

-
- -
-

{% trans 'OpenVPN' %}

-

-

    -
  • Windows, OSX
  • -
  • GNU/Linux, BSD
  • -
  • Android, iOS
  • -
-

-
+
+
+
+

CCrypto VPN is a simple solution for only 3€/month

-
- -
-

{% trans 'Fast' %}

-

{% trans 'Up to 1Gbps.' %}
- {% trans 'With WireGuard compatibility' %} - {% trans 'for the best performances.' %} -

-
+
+
    +
  • Access to fast & secure servers around the world with no data cap.
  • +
  • Powered by OpenVPN or WireGuard, freely available on Windows, Linux, macOS, Android, iOS, and many more.
  • +
  • With the best encryption available, IPv4 & IPv6, private DNS resolvers, and DDoS protection.
  • +
  • Subscribe with PayPal, credit card, or any of 40+ cryptocurrencies
  • +
-
-
-
-

{% trans 'Hide your IP address to disrupt tracking and geolocation.' %}

-
-
-

{% trans 'Protect your privacy on insecure networks with AES GCM 256 bits, RSA 4096 bits.' %}

-
- -
-

{% trans 'Get a clean and neutral access without censorship, interception, or manipulation.' %}

-
-
-

{% trans 'Bypass firewalls in restricted network environments.' %}

-
- -
-

{% trans 'Transparently compress your traffic to save data and gain speed on slow connections.' %}

-
- -
-

{% trans 'Get IPv6 connectivity on IPv4-only networks or where IPv6 is not equally supported.' %}

-
+
+ + {{gateway_locations|json_script:"locations-data"}} + +

+ more on our infrastructure +

+
{% endblock %} - -