From 4919f028c884a041da7ff098abb02389b4eac598 Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Tue, 18 Apr 2023 03:02:17 +0800 Subject: ✨add envshare docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- envshare/app/components/analytics.tsx | 22 ++++++ envshare/app/components/error.tsx | 11 +++ envshare/app/components/stats.tsx | 57 ++++++++++++++++ envshare/app/components/testimony.tsx | 125 ++++++++++++++++++++++++++++++++++ envshare/app/components/title.tsx | 9 +++ 5 files changed, 224 insertions(+) create mode 100644 envshare/app/components/analytics.tsx create mode 100644 envshare/app/components/error.tsx create mode 100644 envshare/app/components/stats.tsx create mode 100644 envshare/app/components/testimony.tsx create mode 100644 envshare/app/components/title.tsx (limited to 'envshare/app/components') diff --git a/envshare/app/components/analytics.tsx b/envshare/app/components/analytics.tsx new file mode 100644 index 0000000..ef6a2ae --- /dev/null +++ b/envshare/app/components/analytics.tsx @@ -0,0 +1,22 @@ +"use client"; +import { Analytics as VercelAnalytics } from "@vercel/analytics/react"; + +const track = ["/", "/share", "/deploy", "/unseal"]; + +export function Analytics() { + return ( + { + const url = new URL(event.url); + if (!track.includes(url.pathname)) { + url.pathname = "/__redacted"; + return { + ...event, + url: url.href, + }; + } + return event; + }} + /> + ); +} diff --git a/envshare/app/components/error.tsx b/envshare/app/components/error.tsx new file mode 100644 index 0000000..acf36d7 --- /dev/null +++ b/envshare/app/components/error.tsx @@ -0,0 +1,11 @@ +type Props = { + message: string; +}; + +export const ErrorMessage: React.FC = ({ message }) => { + return ( +
+ {message} +
+ ); +}; diff --git a/envshare/app/components/stats.tsx b/envshare/app/components/stats.tsx new file mode 100644 index 0000000..31d74bc --- /dev/null +++ b/envshare/app/components/stats.tsx @@ -0,0 +1,57 @@ +import { Redis } from "@upstash/redis"; + +const redis = Redis.fromEnv(); +export const revalidate = 60; + +export const Stats = asyncComponent(async () => { + const [reads, writes] = await redis + .pipeline() + .get("envshare:metrics:reads") + .get("envshare:metrics:writes") + .exec<[number, number]>(); + const stars = await fetch("https://api.github.com/repos/chronark/envshare") + .then((res) => res.json()) + .then((json) => json.stargazers_count as number); + + const stats = [ + { + label: "Documents Encrypted", + value: writes, + }, + { + label: "Documents Decrypted", + value: reads, + }, + ] satisfies { label: string; value: number }[]; + + if (stars) { + stats.push({ + label: "GitHub Stars", + value: stars, + }); + } + + return ( +
+
    + {stats.map(({ label, value }) => ( +
  • +
    + {Intl.NumberFormat("en-US", { notation: "compact" }).format(value)} +
    +
    {label}
    +
  • + ))} +
+
+ ); +}); + +// stupid hack to make "server components" actually work with components +// https://www.youtube.com/watch?v=h_9Vx6kio2s +function asyncComponent(fn: (arg: T) => Promise): (arg: T) => R { + return fn as (arg: T) => R; +} diff --git a/envshare/app/components/testimony.tsx b/envshare/app/components/testimony.tsx new file mode 100644 index 0000000..757a953 --- /dev/null +++ b/envshare/app/components/testimony.tsx @@ -0,0 +1,125 @@ +"use client"; +import Image from "next/image"; +import Link from "next/link"; +import { Props } from "next/script"; +import React, { PropsWithChildren } from "react"; + +const TwitterHandle: React.FC = ({ children }) => { + return {children}; +}; + +const Author: React.FC> = ({ children, href }) => ( + + {children} + +); + +const Title: React.FC> = ({ children, href }) => ( + + {children} + +); + +export const Testimonials = () => { + const posts: { + content: React.ReactNode; + link: string; + author: { + name: React.ReactNode; + title?: React.ReactNode; + image: string; + }; + }[] = [ + { + content: ( +
+

+ My cursory audit of @chronark_'s envshare: +

+

+ It is light, extremely functional, and does its symmetric block cipher correctly, unique initialization + vectors, decryption keys derived securely. +

+
+

Easily modified to remove minimal analytics. Superior to Privnote.

+
+

Self-hosting is easy. 👏

+
+ ), + link: "https://twitter.com/FrederikMarkor/status/1615299856205250560", + author: { + name: Frederik Markor, + title: CEO @discreet, + image: "https://pbs.twimg.com/profile_images/1438061314010664962/NecuMIGR_400x400.jpg", + }, + }, + { + content: ( +
+

I'm particularly chuffed about this launch, for a couple of reasons:

+
    +
  • + ◆ Built on @nextjs + @upstash, hosted on{" "} + @vercel +
  • +
  • ◆ 100% free to use & open source
  • +
  • ◆ One-click deploy via Vercel + Upstash integration
  • +
+

Deploy your own → http://vercel.fyi/envshare

+
+ ), + link: "https://twitter.com/steventey/status/1615035241772482567?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1615035241772482567%7Ctwgr%5E1db44bb10c690189e24c980fcd787299961c34c6%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fpublish.twitter.com%2F%3Fquery%3Dhttps3A2F2Ftwitter.com2Fsteventey2Fstatus2F1615035241772482567widget%3DTweet", + author: { + name: Steven Tey, + title: Senior Developer Advocate at Vercel, + image: "https://pbs.twimg.com/profile_images/1506792347840888834/dS-r50Je_400x400.jpg", + }, + }, + { + content: ( +
+

+ Congratulations on the launch @chronark_👏! This is such a valuable product + for developers. Icing on the cake is that it's open source! ✨ +

+
+ ), + link: "https://twitter.com/DesignSiddharth/status/1615293209164546048", + author: { + name: @DesignSiddharth, + image: "https://pbs.twimg.com/profile_images/1613772710009765888/MbSblJYf_400x400.jpg", + }, + }, + ]; + + return ( +
+
    + {posts.map((post, i) => ( +
    + + {post.content} + +
    +
    +
    {post.author.name}
    +
    {post.author.title}
    +
    +
    + +
    +
    +
    + ))} +
+
+ ); +}; diff --git a/envshare/app/components/title.tsx b/envshare/app/components/title.tsx new file mode 100644 index 0000000..b9b7f3c --- /dev/null +++ b/envshare/app/components/title.tsx @@ -0,0 +1,9 @@ +import React, { PropsWithChildren } from "react"; + +export const Title: React.FC = ({ children }): JSX.Element => { + return ( +

+ {children} +

+ ); +}; -- cgit v1.2.3-70-g09d2