api updates
This commit is contained in:
parent
c8b1bd07d2
commit
18a75fcc24
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { ChevronDown, ChevronRight, ChevronUp, Copy, DeviceFloppy, Moon, Sun, TextPlus } from 'svelte-tabler';
|
||||
import { ChevronDown, ChevronRight, ChevronUp, Copy, DeviceFloppy, InfoCircleFilled, Moon, Sun, TextPlus } from 'svelte-tabler';
|
||||
import { storeHighlightJs } from '@skeletonlabs/skeleton';
|
||||
import { CodeBlock } from '@skeletonlabs/skeleton';
|
||||
import ToolBox from '$lib/components/ToolBox.svelte';
|
||||
|
@ -98,7 +98,7 @@
|
|||
>. To use the script:
|
||||
</p>
|
||||
|
||||
<CodeBlock language="bash" code={`curl -O https://0x45.st/paste69.sh && chmod +x paste69.sh
|
||||
<pre class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg"><code class="language-bash">curl -O https://0x45.st/paste69.sh && chmod +x paste69.sh
|
||||
./paste69.sh --help
|
||||
|
||||
# Paste69 CLI script
|
||||
|
@ -113,46 +113,78 @@
|
|||
# -p, --password <password> Set a password for the paste. This enables encryption.
|
||||
# -x, --burn Burn the paste after it is viewed once.
|
||||
# -r, --raw Return the raw JSON response.
|
||||
# -c, --copy Copy the paste URL to the clipboard.`}></CodeBlock>
|
||||
# -c, --copy Copy the paste URL to the clipboard.</code></pre>
|
||||
|
||||
<p class="mb-4 mt-4">To create a paste with the script, simply pipe the contents of a file to the script:</p>
|
||||
|
||||
<CodeBlock language="bash" code={`cat file.md | ./paste69.sh
|
||||
# https://0x45.st/some-random-id.md`}></CodeBlock>
|
||||
<code class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg">cat file.md | ./paste69.sh
|
||||
# https://0x45.st/some-random-id.md</code>
|
||||
|
||||
<h2 id="api" class="h2 mt-4 mb-2"><a class="underline hover:text-gray-300" href="#api">API</a></h2>
|
||||
|
||||
<p class="mb-4 mt-4">Paste69 has a simple API for creating and fetching pastes. The API is documented below.</p>
|
||||
<p class="mb-4 mt-4">
|
||||
Paste69 has a simple API for creating and fetching pastes. The API accepts JSON, form data, and plain text with
|
||||
query parameters. The API will respond with JSON or plain text, depenant on the state of the `raw`
|
||||
parameter.
|
||||
</p>
|
||||
|
||||
<aside class="alert variant-filled-tertiary my-6">
|
||||
<!-- Icon -->
|
||||
<div><InfoCircleFilled size="42" /></div>
|
||||
<!-- Message -->
|
||||
<div class="alert-message">
|
||||
<h3 class="h3">Note</h3>
|
||||
<p>
|
||||
The below examples are offered as curl commands to make things simple, but you can use whatever tool you want to
|
||||
make requests to the API.
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<h3 id="api-creating-a-paste" class="h3 mt-4 mb-2">
|
||||
<a class="underline hover:text-gray-300" href="#api-creating-a-paste">Creating a Paste</a>
|
||||
</h3>
|
||||
|
||||
<p class="mb-4 mt-4">
|
||||
To create a paste, send a POST request to <code>/api/pastes</code>
|
||||
{' '}
|
||||
with the following JSON body:
|
||||
To create a paste, send a POST request to <code>/api/pastes</code>. Valid parameters are as follows:
|
||||
</p>
|
||||
|
||||
<CodeBlock language="json" code={`{
|
||||
"contents": "paste contents",
|
||||
"language": "txt",
|
||||
"encrypt": false,
|
||||
"password": "",
|
||||
"burnAfterReading": false,
|
||||
}`}></CodeBlock>
|
||||
<ul class="list-disc list-inside mb-4">
|
||||
<li>
|
||||
<code>contents</code> - The contents of the paste.
|
||||
</li>
|
||||
<li>
|
||||
<code>language</code> - The language of the paste. This is used for syntax highlighting. If no language is specified, the
|
||||
API will attempt to detect the language.
|
||||
</li>
|
||||
<li>
|
||||
<code>password</code> - A password to encrypt the paste with. This will enable encryption.
|
||||
</li>
|
||||
<li>
|
||||
<code>burnAfterReading</code> - A boolean value to enable burn after reading. If this is set to true, the paste will be deleted
|
||||
after it is viewed once.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="h4 mt-4 mb-2">Examples</h4>
|
||||
|
||||
<code class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg">curl -X POST -H "Content-Type: application/json" -d '{'{'}"contents": "paste contents"{'}'}' https://0x45.st/api/pastes`}></code>
|
||||
|
||||
<code class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg">curl -X POST -F "contents=paste contents" https://0x45.st/api/pastes`}></code>
|
||||
|
||||
<code class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg">curl -X POST -d "paste contents" https://0x45.st/api/pastes`}></code>
|
||||
|
||||
<p class="mb-4 mt-4">If the paste was successfully created, the API will respond with the following JSON:</p>
|
||||
|
||||
<CodeBlock language="json" code={`{
|
||||
"id": "paste id",
|
||||
<pre class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg"><code class="language-json">{'{'}
|
||||
"id": "paste id",
|
||||
"url": "https://0x45.st/some-random-id.md",
|
||||
"contents": "paste contents",
|
||||
"highlight": "txt",
|
||||
"contents": "paste contents",
|
||||
"highlight": "txt",
|
||||
"encrypted": false,
|
||||
"burnAfterReading": false,
|
||||
"created_at": "2021-08-05T07:30:00.000Z",
|
||||
}`}></CodeBlock>
|
||||
"created_at": "2021-08-05T07:30:00.000Z",
|
||||
{'}'}</code></pre>
|
||||
|
||||
<h3 id="api-fetching-a-paste" class="h3 mt-4 mb-2">
|
||||
<a class="underline hover:text-gray-300" href="#api-fetching-a-paste">Fetching a Paste</a>
|
||||
|
@ -163,15 +195,15 @@
|
|||
<code>/api/pastes/:id</code>. If the paste exists, the API will respond with the following JSON:
|
||||
</p>
|
||||
|
||||
<CodeBlock language="json" code={`{
|
||||
"id": "paste id",
|
||||
<pre class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg"><code class="language-json">{'{'}
|
||||
"id": "paste id",
|
||||
"url": "https://0x45.st/some-random-id.md",
|
||||
"contents": "paste contents",
|
||||
"highlight": "txt",
|
||||
"contents": "paste contents",
|
||||
"highlight": "txt",
|
||||
"encrypted": false,
|
||||
"burnAfterReading": false,
|
||||
"created_at": "2021-08-05T07:30:00.000Z",
|
||||
}`}></CodeBlock>
|
||||
"created_at": "2021-08-05T07:30:00.000Z",
|
||||
{'}'}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
||||
|
|
|
@ -1,13 +1,111 @@
|
|||
import { detectLanguage } from "$utils/hljs";
|
||||
import { encrypt as doEncrypt } from "$utils/crypto";
|
||||
import { error, json, type RequestHandler } from "@sveltejs/kit";
|
||||
import { error, json, text, type RequestHandler } from "@sveltejs/kit";
|
||||
import { generate } from 'random-words';
|
||||
import { Mongo } from "$lib/db/index";
|
||||
import { env } from "$env/dynamic/private";
|
||||
import { extensionMap } from "$utils/languages";
|
||||
|
||||
interface PasteOptions {
|
||||
contents?: string;
|
||||
language?: string;
|
||||
encrypt?: boolean;
|
||||
password?: string;
|
||||
burnAfterReading?: boolean;
|
||||
raw?: boolean;
|
||||
}
|
||||
|
||||
const isTrue = (value: string | boolean | undefined): boolean => {
|
||||
if (typeof value === 'boolean') {
|
||||
return value;
|
||||
} else if (typeof value === 'string') {
|
||||
return ['true', '1', 'yes', 'y', 'on'].includes(value.toLowerCase());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the options from the form data.
|
||||
const extractOptionsFromForm = async (req: Request): Promise<PasteOptions> => {
|
||||
const form = await req.formData();
|
||||
const contents = form.get('contents') as string;
|
||||
const language = form.get('language') as string;
|
||||
const password = form.get('password') as string;
|
||||
const burnAfterReading = form.get('burnAfterReading') as string;
|
||||
const raw = form.get('raw') as string;
|
||||
|
||||
if (!contents || contents.length === 0) {
|
||||
throw error(400, 'No contents provided for your paste.')
|
||||
}
|
||||
|
||||
return {
|
||||
contents,
|
||||
language,
|
||||
password,
|
||||
burnAfterReading: isTrue(burnAfterReading),
|
||||
raw: isTrue(raw),
|
||||
};
|
||||
};
|
||||
|
||||
// Extract the options from the JSON body.
|
||||
const extractOptionsFromJSON = async (req: Request): Promise<PasteOptions> => {
|
||||
const { contents, language, password, burnAfterReading, raw } = await req.json();
|
||||
console.log(contents, language, password, burnAfterReading, raw);
|
||||
|
||||
if (!contents || contents.length === 0) {
|
||||
throw error(400, 'No contents provided for your paste.')
|
||||
}
|
||||
|
||||
return {
|
||||
contents,
|
||||
language,
|
||||
password,
|
||||
burnAfterReading: isTrue(burnAfterReading),
|
||||
raw: isTrue(raw),
|
||||
};
|
||||
};
|
||||
|
||||
// Extract the options from the query string, and body.
|
||||
const extractOptionsFromQuery = async (req: Request): Promise<PasteOptions> => {
|
||||
const url = new URL(req.url);
|
||||
const query = url.searchParams;
|
||||
|
||||
const reader = req.body?.getReader();
|
||||
const body = await reader?.read();
|
||||
const contents = body?.value?.toString();
|
||||
|
||||
if (!contents) {
|
||||
throw error(400, 'No contents provided for your paste.')
|
||||
}
|
||||
|
||||
const language = query.get('language') as string;
|
||||
const password = query.get('password') as string;
|
||||
const burnAfterReading = query.get('burnAfterReading') as string;
|
||||
const raw = query.get('raw') as string;
|
||||
|
||||
return {
|
||||
contents,
|
||||
language,
|
||||
password,
|
||||
burnAfterReading: isTrue(burnAfterReading),
|
||||
raw: isTrue(raw),
|
||||
};
|
||||
};
|
||||
|
||||
// Extract the options from the request, depending on the content type.
|
||||
const extractOptions = async (req: Request): Promise<PasteOptions> => {
|
||||
const contentType = req.headers.get('content-type') || '';
|
||||
if (contentType.includes('form')) {
|
||||
return await extractOptionsFromForm(req);
|
||||
} else if (contentType.includes('json')) {
|
||||
return await extractOptionsFromJSON(req);
|
||||
} else {
|
||||
return await extractOptionsFromQuery(req);
|
||||
}
|
||||
};
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const { contents, language, encrypt, password, burnAfterReading } = await request.json();
|
||||
const { contents, language, password, burnAfterReading, raw } = await extractOptions(request);
|
||||
const id = generate({ exactly: 3, join: '-' });
|
||||
|
||||
if (!contents || contents.length === 0) {
|
||||
|
@ -28,9 +126,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||
let pasteContents: string = contents;
|
||||
const highlight = language || detectLanguage(contents) || 'txt';
|
||||
|
||||
if (encrypt && !password) {
|
||||
throw error(400, 'No password provided for encryption.');
|
||||
} else if (encrypt) {
|
||||
if (password) {
|
||||
pasteContents = await doEncrypt(contents, password);
|
||||
}
|
||||
|
||||
|
@ -38,7 +134,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||
id,
|
||||
url: `${env.SITE_URL}/${id}.${highlight}`,
|
||||
highlight,
|
||||
encrypted: !!encrypt,
|
||||
encrypted: !!password,
|
||||
contents: pasteContents,
|
||||
burnAfterReading: !!burnAfterReading,
|
||||
createdAt: new Date(),
|
||||
|
@ -51,7 +147,11 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||
throw error(500, 'Failed to create paste');
|
||||
}
|
||||
|
||||
return json(data, {
|
||||
status: 201,
|
||||
})
|
||||
if (raw) {
|
||||
return json(data, {
|
||||
status: 201,
|
||||
});
|
||||
} else {
|
||||
return text(data.url);
|
||||
}
|
||||
};
|
|
@ -53,6 +53,7 @@ function show_help {
|
|||
|
||||
burn=false
|
||||
encrypt=false
|
||||
raw=false
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
|
@ -134,7 +135,8 @@ json=$(jq -n \
|
|||
--arg password "$password" \
|
||||
--argjson encrypt $encrypt \
|
||||
--argjson burn $burn \
|
||||
'{"contents": $contents, "extension": $extension, "language": $language, "password": $password, "encrypt": $encrypt, "burnAfterReading": $burn}')
|
||||
--argjson raw $raw \
|
||||
'{"contents": $contents, "extension": $extension, "language": $language, "password": $password, "encrypt": $encrypt, "burnAfterReading": $burn, "raw": $raw}')
|
||||
|
||||
response=$(
|
||||
curl -s -X POST $url \
|
||||
|
@ -148,24 +150,20 @@ if [ $? -ne 0 ]; then
|
|||
fi
|
||||
|
||||
# Check if there is an error
|
||||
error=$(echo $response | jq -r '.error')
|
||||
if [ "$error" != "null" ]; then
|
||||
error=$(echo "$response" | jq -r '.message' 2>/dev/null || true)
|
||||
if [ -n "$error" ] && [ "$error" != "null" ]; then
|
||||
echo "Error: $error"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If raw is set, return the raw JSON response
|
||||
if [ ! -z "$raw" ]; then
|
||||
paste_url=$response
|
||||
if [ "$raw" = true ]; then
|
||||
paste_url=$(echo $response | jq -r '.url')
|
||||
echo $response
|
||||
exit 0
|
||||
else
|
||||
echo $paste_url
|
||||
fi
|
||||
|
||||
# Get the paste URL from the response
|
||||
paste_url=$(echo $response | jq -r '.url')
|
||||
|
||||
# Print the paste URL
|
||||
echo $paste_url
|
||||
|
||||
# If copy is set, copy the paste URL to the clipboard
|
||||
if [ ! -z "$copy" ]; then
|
||||
if [ ! -z "$clipboard" ]; then
|
||||
|
|
|
@ -16,6 +16,10 @@ const config = {
|
|||
alias: {
|
||||
'$db/*': './src/lib/db/*',
|
||||
'$utils/*': './src/utils/*',
|
||||
},
|
||||
|
||||
csrf: {
|
||||
checkOrigin: false,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue