WIP: testing bright and resume copying content
This commit is contained in:
parent
cf0579023e
commit
a607a4528e
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,7 +25,7 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env*
|
.env.development
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|||||||
2
.tool-versions
Normal file
2
.tool-versions
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
nodejs 20.8.1
|
||||||
|
pnpm 8.9.2
|
||||||
@ -8,6 +8,8 @@ import parse, {
|
|||||||
HTMLReactParserOptions,
|
HTMLReactParserOptions,
|
||||||
} from "html-react-parser";
|
} from "html-react-parser";
|
||||||
import { DummyPostSlug, DummyPostString } from "@/components/dummyPost";
|
import { DummyPostSlug, DummyPostString } from "@/components/dummyPost";
|
||||||
|
import { Code } from "bright";
|
||||||
|
import { mark } from "@/bright-extension/mark";
|
||||||
|
|
||||||
const options: HTMLReactParserOptions = {
|
const options: HTMLReactParserOptions = {
|
||||||
replace: (domNode) => {
|
replace: (domNode) => {
|
||||||
@ -22,41 +24,46 @@ const options: HTMLReactParserOptions = {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<h1 className={`${raleway.className} mx-auto w-224 text-3xl`}>
|
<h1 className={`${raleway.className} mx-auto w-224 text-3xl`}>
|
||||||
{domToReact(domNode.children)}
|
{domToReact(domNode.children)}
|
||||||
</h1>
|
</h1>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (domNode.name === "h2") {
|
} else if (domNode.name === "h2") {
|
||||||
return (
|
return (
|
||||||
<h1 className={`${raleway.className} mx-auto w-224 text-2xl`}>
|
<h1 className={`${raleway.className} mx-auto w-224 text-2xl`}>
|
||||||
{domToReact(domNode.children)}
|
{domToReact(domNode.children)}
|
||||||
</h1>
|
</h1>
|
||||||
);
|
);
|
||||||
} else if (domNode.name === "h3") {
|
} else if (domNode.name === "h3") {
|
||||||
return (
|
return (
|
||||||
<h1 className={`${raleway.className} mx-auto w-224 text-xl`}>
|
<h1 className={`${raleway.className} mx-auto w-224 text-xl`}>
|
||||||
{domToReact(domNode.children)}
|
{domToReact(domNode.children)}
|
||||||
</h1>
|
</h1>
|
||||||
|
);
|
||||||
|
} else if (domNode.name === "code") {
|
||||||
|
let linenumber = false
|
||||||
|
if ('class' in domNode.attribs) {
|
||||||
|
const classes = domNode.attribs.class.split(" ");
|
||||||
|
if (classes.includes('linenumber')) {
|
||||||
|
linenumber = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={`w-224 mx-auto`}>
|
||||||
|
<Code lang={`${domNode.attribs.lang}`} lineNumbers={linenumber} extensions={[mark]}>
|
||||||
|
{domToReact(domNode.children)}
|
||||||
|
</Code>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
} else if (domNode.name === "p") {
|
} else if (domNode.name === "p") {
|
||||||
|
// console.log(domNode.children)
|
||||||
if (domNode.attribs.class === "paragraph") {
|
if (domNode.attribs.class === "paragraph") {
|
||||||
return (
|
return (
|
||||||
<h1 className={`${nunito_sans.className} mx-auto w-224`}>
|
<h1 className={`${nunito_sans.className} mx-auto w-224`}>
|
||||||
{domToReact(domNode.children)}
|
{domToReact(domNode.children)}
|
||||||
</h1>
|
</h1>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
const classes = domNode.attribs.class.split(" ");
|
|
||||||
if (classes.includes("code")) {
|
|
||||||
if (classes.includes("shell")) {
|
|
||||||
|
|
||||||
} else if (classes.includes("go") || classes.includes("golang")) {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,7 +76,7 @@ export default async function Post({ params }: { params: { slug: string } }) {
|
|||||||
const dummySlug = await DummyPostSlug();
|
const dummySlug = await DummyPostSlug();
|
||||||
if (dummySlug === params.slug) {
|
if (dummySlug === params.slug) {
|
||||||
content = await DummyPostString();
|
content = await DummyPostString();
|
||||||
console.log(content);
|
// console.log(content);
|
||||||
} else {
|
} else {
|
||||||
content = await getPost(params.slug);
|
content = await getPost(params.slug);
|
||||||
}
|
}
|
||||||
@ -78,5 +85,9 @@ export default async function Post({ params }: { params: { slug: string } }) {
|
|||||||
// console.log(content)
|
// console.log(content)
|
||||||
const elem = parse(content, options);
|
const elem = parse(content, options);
|
||||||
|
|
||||||
return <div className={`flex flex-col`}>{elem}</div>;
|
return (
|
||||||
|
<div className={`flex flex-col`}>
|
||||||
|
{elem}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
11
bright-extension/lineNumbers.tsx
Normal file
11
bright-extension/lineNumbers.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Extension } from 'bright';
|
||||||
|
|
||||||
|
export const lineNumbers :Extension = {
|
||||||
|
name: "lineNumbers",
|
||||||
|
beforeHighlight: (props, annotations) => {
|
||||||
|
console.log(annotations);
|
||||||
|
if (annotations.length > 0 ) {
|
||||||
|
return { ...props, lineNumbers: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
bright-extension/mark.tsx
Normal file
15
bright-extension/mark.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Extension } from "bright";
|
||||||
|
|
||||||
|
export const mark: Extension = {
|
||||||
|
name: "mark",
|
||||||
|
InlineAnnotation: ({ children, query }) => {
|
||||||
|
return (
|
||||||
|
<mark style={{ background: query }}>{children}</mark>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
MultilineAnnotation: ({ children, query }) => {
|
||||||
|
return (
|
||||||
|
<div style={{ background: query }}>{children}</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -1,78 +1,13 @@
|
|||||||
|
import { promises as fsp } from 'fs'
|
||||||
|
|
||||||
export async function DummyPostString() {
|
export async function DummyPostString() {
|
||||||
const ReactDOMServer = (await import('react-dom/server')).default
|
let path = ""
|
||||||
const component = await DummyPost()
|
if ('DUMMY_HTML_DIR' in process.env && typeof process.env.DUMMY_HTML_DIR === "string") {
|
||||||
return ReactDOMServer.renderToStaticMarkup(component)
|
path = process.env.DUMMY_HTML_DIR + "test1.html";
|
||||||
|
}
|
||||||
|
return await fsp.readFile(path, "utf-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DummyPostSlug() {
|
export async function DummyPostSlug() {
|
||||||
return "dummy-post"
|
return "dummy-post"
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function DummyPost() {
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<h1 className="title">Nginx + SSL Client Certificate Verification: Manage access to a site</h1>
|
|
||||||
<p className="paragraph">Access control is a fundamental part of security. Most entities rely on
|
|
||||||
the combination of username and password, sometimes with additional multi-factor authentication
|
|
||||||
to improve security. Some entities also use the SSL client certificate verification to manage access
|
|
||||||
to specific resources. One of the use cases where SSL client certificate verification fits perfectly is
|
|
||||||
managing access to internet-facing development or staging servers. In this post, I'll share how
|
|
||||||
to set up the certificates and configure nginx to verify users based on their certificates.</p>
|
|
||||||
<h1>Preparing the certificates</h1>
|
|
||||||
<p className="paragraph">There are two certificates we are going to create. The first one is the root
|
|
||||||
certificate. It will be placed in the Nginx server. The second one is the client certificate. It will
|
|
||||||
be installed in the client machine/browsers.</p>
|
|
||||||
<h2>Root CA</h2>
|
|
||||||
<p className="paragraph">For generating a root CA, execute these two steps:</p>
|
|
||||||
<h3>Generate RSA Key</h3>
|
|
||||||
<p className="code">openssl genrsa -aes256 -out ca.key 4096</p>
|
|
||||||
<h3>Create Root CA crt file.</h3>
|
|
||||||
<p className="code">openssl req -new -x509 -days 3650 -key ca.key -out ca.crt</p>
|
|
||||||
<h2>Setup CA configuration</h2>
|
|
||||||
<p className="paragraph">This is an optional step, but if you want to be able to revoke access you
|
|
||||||
previously granted, you need to do this step.</p>
|
|
||||||
<p className="paragraph">Create a file named ca.cnf in the same directory as the ca.key and ca.crt.</p>
|
|
||||||
<p className="code">[ ca ]
|
|
||||||
default_ca = gca
|
|
||||||
|
|
||||||
[ crl_ext ]
|
|
||||||
authorityKeyIdentifier=keyid:always
|
|
||||||
|
|
||||||
[ gca ]
|
|
||||||
dir = ./
|
|
||||||
new_certs_dir = $dir
|
|
||||||
unique_subject = no
|
|
||||||
certificate = $dir/ca.crt
|
|
||||||
database = $dir/certindex
|
|
||||||
private_key = $dir/ca.key
|
|
||||||
serial = $dir/certserial
|
|
||||||
default_days = 365
|
|
||||||
default_md = sha256
|
|
||||||
policy = gca_policy
|
|
||||||
x509_extensions = gca_extensions
|
|
||||||
crlnumber = $dir/crlnumber
|
|
||||||
default_crl_days = 365
|
|
||||||
|
|
||||||
[ gca_policy ]
|
|
||||||
commonName = supplied
|
|
||||||
stateOrProvinceName = supplied
|
|
||||||
countryName = optional
|
|
||||||
emailAddress = optional
|
|
||||||
organizationName = supplied
|
|
||||||
organizationUnitName = optional
|
|
||||||
|
|
||||||
[ gca_extensions ]
|
|
||||||
basicConstraints = CA:false
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid:always
|
|
||||||
keyUsage = digitalSignature,keyEncipherment
|
|
||||||
extendedKeyUsage = serverAuth
|
|
||||||
crlDistributionPoints = URI:http://example.com/root.crl
|
|
||||||
subjectAltName = @alt_names
|
|
||||||
|
|
||||||
[ alt_names ]
|
|
||||||
DNS.1 = example.com
|
|
||||||
DNS.2 = *.example.com</p>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
130
dummies/test1.html
Normal file
130
dummies/test1.html
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<h1 class="title">Nginx + SSL Client Certificate Verification: Manage access to a site</h1>
|
||||||
|
<p class="paragraph">Access control is a fundamental part of security. Most entities rely on
|
||||||
|
the combination of username and password, sometimes with additional multi-factor authentication
|
||||||
|
to improve security. Some entities also use the SSL client certificate verification to manage access
|
||||||
|
to specific resources. One of the use cases where SSL client certificate verification fits perfectly is
|
||||||
|
managing access to internet-facing development or staging servers. In this post, I'll share how
|
||||||
|
to set up the certificates and configure nginx to verify users based on their certificates.</p>
|
||||||
|
<h1>Preparing the certificates</h1>
|
||||||
|
<p class="paragraph">There are two certificates we are going to create. The first one is the root
|
||||||
|
certificate. It will be placed in the Nginx server. The second one is the client certificate. It will
|
||||||
|
be installed in the client machine/browsers.</p>
|
||||||
|
<h2>Root CA</h2>
|
||||||
|
<p class="paragraph">For generating a root CA, execute these two steps:</p>
|
||||||
|
<h3>Generate RSA Key</h3>
|
||||||
|
<code lang="shell">openssl genrsa -aes256 -out ca.key 4096</code>
|
||||||
|
<h3>Create Root CA crt file.</h3>
|
||||||
|
<code lang="shell">openssl req -new -x509 -days 3650 -key ca.key -out ca.crt</code>
|
||||||
|
<h2>Setup CA configuration</h2>
|
||||||
|
<p class="paragraph">This is an optional step, but if you want to be able to revoke access you
|
||||||
|
previously granted, you need to do this step.</p>
|
||||||
|
<p class="paragraph">Create a file named ca.cnf in the same directory as the ca.key and ca.crt.</p>
|
||||||
|
<code lang="ini" class="linenumber">[ ca ]
|
||||||
|
default_ca = gca
|
||||||
|
|
||||||
|
[ crl_ext ]
|
||||||
|
authorityKeyIdentifier=keyid:always
|
||||||
|
|
||||||
|
[ gca ]
|
||||||
|
dir = ./
|
||||||
|
new_certs_dir = $dir
|
||||||
|
unique_subject = no
|
||||||
|
certificate = $dir/ca.crt
|
||||||
|
database = $dir/certindex
|
||||||
|
; mark(1[9:11]) dimgrey
|
||||||
|
private_key = $dir/ca.key
|
||||||
|
serial = $dir/certserial
|
||||||
|
default_days = 365
|
||||||
|
default_md = sha256
|
||||||
|
policy = gca_policy
|
||||||
|
x509_extensions = gca_extensions
|
||||||
|
crlnumber = $dir/crlnumber
|
||||||
|
default_crl_days = 365
|
||||||
|
|
||||||
|
[ gca_policy ]
|
||||||
|
commonName = supplied
|
||||||
|
stateOrProvinceName = supplied
|
||||||
|
countryName = optional
|
||||||
|
emailAddress = optional
|
||||||
|
organizationName = supplied
|
||||||
|
organizationUnitName = optional
|
||||||
|
|
||||||
|
[ gca_extensions ]
|
||||||
|
basicConstraints = CA:false
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid:always
|
||||||
|
keyUsage = digitalSignature,keyEncipherment
|
||||||
|
extendedKeyUsage = serverAuth
|
||||||
|
crlDistributionPoints = URI:http://example.com/root.crl
|
||||||
|
subjectAltName = @alt_names
|
||||||
|
|
||||||
|
[ alt_names ]
|
||||||
|
DNS.1 = example.com
|
||||||
|
DNS.2 = *.example.com</code>
|
||||||
|
<p class="paragraph">Initialize an empty file for the CA database.</p>
|
||||||
|
<code lang="shell">touch certindex</code>
|
||||||
|
<p class="paragraph">Initialize value for certserial and crlnumber</p>
|
||||||
|
<code lang="shell">echo 01 > certserial
|
||||||
|
echo 01 > crlnumber</code>
|
||||||
|
<h2>User Certificates</h2>
|
||||||
|
<h3>Generate the user RSA key.</h3>
|
||||||
|
<code lang="shell">openssl genrsa -aes256 -out client01/user.key 4096</code>
|
||||||
|
<h3>Create Certificate-Signing Request (CSR)</h3>
|
||||||
|
<code lang="shell">openssl req -new -key client01/user.key -out client01/user.csr</code>
|
||||||
|
<h3>Sign the CSR.</h3>
|
||||||
|
<p class="paragraph">If you did the setup CA configuration step, sign the CSR file by running this command.</p>
|
||||||
|
<code lang="shell">openssl ca -config ca.cnf -in client01/user.csr -out client01/user.crt</code>
|
||||||
|
<p class="paragraph">If you skipped the setup CA configuration step, sign the CSR file by running this command.</p>
|
||||||
|
<code lang="shell">openssl x509 -req -days 365 -in client01/user.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client01/user.crt</code>
|
||||||
|
<h3>Convert the crt file to pfx/p12 file.</h3>
|
||||||
|
<p class="paragraph">Most of the time, browsers/client machines only accept a certificate in the pfx format. Run this
|
||||||
|
command to convert the crt file to the pfx/p12 format.</p>
|
||||||
|
<code lang="shell">openssl pkcs12 -export -out client01/user.pfx -inkey client01/user.key -in client01/user.crt -certfile ca.crt</code>
|
||||||
|
<p class="paragraph">You'll be prompted to enter an export password. You must input the exact password when adding
|
||||||
|
the certificate to a browser.</p>
|
||||||
|
<br/>
|
||||||
|
<h1>Setting up nginx with client certificates verification</h1>
|
||||||
|
<p class="paragraph">Add these lines to a server block in your nginx configuration</p>
|
||||||
|
<code lang="shell" class="linenumber">ssl_client_certificate /path/to/client/verfication/ca.crt;
|
||||||
|
ssl_verify_client optional;
|
||||||
|
ssl_verify_depth 2;</code>
|
||||||
|
<p class="paragraph">You can do location-based access control. Location-based here refers to a location block in your
|
||||||
|
nginx configuration, for example:</p>
|
||||||
|
<code lang="shell"> location /private {
|
||||||
|
# mark(1[13:41]) dimgrey
|
||||||
|
if ($ssl_client_verify != SUCCESS) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
....
|
||||||
|
|
||||||
|
}
|
||||||
|
</code>
|
||||||
|
<p class="paragraph">Here is a complete example of a server block in the nginx configuration</p>
|
||||||
|
<code lang="shell">server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
|
||||||
|
server_name www.example.com;
|
||||||
|
ssl_certificate /path/to/your/https/certificate.pem;
|
||||||
|
ssl_certificate_key /path/to/your/https/private-key.pem;
|
||||||
|
include snippets/ssl-params.conf;
|
||||||
|
|
||||||
|
# mark(1:3) dimgrey
|
||||||
|
ssl_client_certificate /path/to/client/verification/ca.crt;
|
||||||
|
ssl_verify_client optional;
|
||||||
|
ssl_verify_depth 2;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# mark(1[13:41]) dimgrey
|
||||||
|
if ($ssl_client_verify != SUCCESS) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}</code>
|
||||||
|
<br/>
|
||||||
|
<h1>Adding the User Certificates to the client machine/browsers</h1>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
"@types/react": "18.2.22",
|
"@types/react": "18.2.22",
|
||||||
"@types/react-dom": "18.2.7",
|
"@types/react-dom": "18.2.7",
|
||||||
"autoprefixer": "10.4.16",
|
"autoprefixer": "10.4.16",
|
||||||
|
"bright": "^0.8.4",
|
||||||
"dompurify": "^3.0.6",
|
"dompurify": "^3.0.6",
|
||||||
"eslint": "8.50.0",
|
"eslint": "8.50.0",
|
||||||
"eslint-config-next": "13.5.2",
|
"eslint-config-next": "13.5.2",
|
||||||
|
|||||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@ -23,6 +23,9 @@ dependencies:
|
|||||||
autoprefixer:
|
autoprefixer:
|
||||||
specifier: 10.4.16
|
specifier: 10.4.16
|
||||||
version: 10.4.16(postcss@8.4.30)
|
version: 10.4.16(postcss@8.4.30)
|
||||||
|
bright:
|
||||||
|
specifier: ^0.8.4
|
||||||
|
version: 0.8.4(react@18.2.0)
|
||||||
dompurify:
|
dompurify:
|
||||||
specifier: ^3.0.6
|
specifier: ^3.0.6
|
||||||
version: 3.0.6
|
version: 3.0.6
|
||||||
@ -87,6 +90,10 @@ packages:
|
|||||||
regenerator-runtime: 0.14.0
|
regenerator-runtime: 0.14.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@code-hike/lighter@0.8.1:
|
||||||
|
resolution: {integrity: sha512-St4rPmB7C2EWmAK1sAbvD3lZeM7UDInVDMjQDzEDsu4Q3B3AqF25vXedQK51U0UO0MCOASgBBdTiNwvJAfIqMQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.50.0):
|
/@eslint-community/eslint-utils@4.4.0(eslint@8.50.0):
|
||||||
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
@ -685,6 +692,16 @@ packages:
|
|||||||
fill-range: 7.0.1
|
fill-range: 7.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/bright@0.8.4(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-SBlcJje8EDh3lLihqr0W27yHCVpuh3NReziNxZOwUzuaShqq+V/imY+J+AvJdTIOifiCQkyIsgVWKAj5G/eOyg==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18
|
||||||
|
dependencies:
|
||||||
|
'@code-hike/lighter': 0.8.1
|
||||||
|
react: 18.2.0
|
||||||
|
server-only: 0.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/browserslist@4.21.11:
|
/browserslist@4.21.11:
|
||||||
resolution: {integrity: sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==}
|
resolution: {integrity: sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==}
|
||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
@ -2664,6 +2681,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
|
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/server-only@0.0.1:
|
||||||
|
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/set-function-name@2.0.1:
|
/set-function-name@2.0.1:
|
||||||
resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
|
resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user