api updates
This commit is contained in:
parent
c8b1bd07d2
commit
18a75fcc24
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<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 { storeHighlightJs } from '@skeletonlabs/skeleton';
|
||||||
import { CodeBlock } from '@skeletonlabs/skeleton';
|
import { CodeBlock } from '@skeletonlabs/skeleton';
|
||||||
import ToolBox from '$lib/components/ToolBox.svelte';
|
import ToolBox from '$lib/components/ToolBox.svelte';
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
>. To use the script:
|
>. To use the script:
|
||||||
</p>
|
</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.sh --help
|
||||||
|
|
||||||
# Paste69 CLI script
|
# Paste69 CLI script
|
||||||
|
@ -113,38 +113,70 @@
|
||||||
# -p, --password <password> Set a password for the paste. This enables encryption.
|
# -p, --password <password> Set a password for the paste. This enables encryption.
|
||||||
# -x, --burn Burn the paste after it is viewed once.
|
# -x, --burn Burn the paste after it is viewed once.
|
||||||
# -r, --raw Return the raw JSON response.
|
# -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>
|
<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
|
<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`}></CodeBlock>
|
# 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>
|
<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">
|
<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>
|
<a class="underline hover:text-gray-300" href="#api-creating-a-paste">Creating a Paste</a>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<p class="mb-4 mt-4">
|
<p class="mb-4 mt-4">
|
||||||
To create a paste, send a POST request to <code>/api/pastes</code>
|
To create a paste, send a POST request to <code>/api/pastes</code>. Valid parameters are as follows:
|
||||||
{' '}
|
|
||||||
with the following JSON body:
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<CodeBlock language="json" code={`{
|
<ul class="list-disc list-inside mb-4">
|
||||||
"contents": "paste contents",
|
<li>
|
||||||
"language": "txt",
|
<code>contents</code> - The contents of the paste.
|
||||||
"encrypt": false,
|
</li>
|
||||||
"password": "",
|
<li>
|
||||||
"burnAfterReading": false,
|
<code>language</code> - The language of the paste. This is used for syntax highlighting. If no language is specified, the
|
||||||
}`}></CodeBlock>
|
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>
|
<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={`{
|
<pre class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg"><code class="language-json">{'{'}
|
||||||
"id": "paste id",
|
"id": "paste id",
|
||||||
"url": "https://0x45.st/some-random-id.md",
|
"url": "https://0x45.st/some-random-id.md",
|
||||||
"contents": "paste contents",
|
"contents": "paste contents",
|
||||||
|
@ -152,7 +184,7 @@
|
||||||
"encrypted": false,
|
"encrypted": false,
|
||||||
"burnAfterReading": false,
|
"burnAfterReading": false,
|
||||||
"created_at": "2021-08-05T07:30:00.000Z",
|
"created_at": "2021-08-05T07:30:00.000Z",
|
||||||
}`}></CodeBlock>
|
{'}'}</code></pre>
|
||||||
|
|
||||||
<h3 id="api-fetching-a-paste" class="h3 mt-4 mb-2">
|
<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>
|
<a class="underline hover:text-gray-300" href="#api-fetching-a-paste">Fetching a Paste</a>
|
||||||
|
@ -163,7 +195,7 @@
|
||||||
<code>/api/pastes/:id</code>. If the paste exists, the API will respond with the following JSON:
|
<code>/api/pastes/:id</code>. If the paste exists, the API will respond with the following JSON:
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<CodeBlock language="json" code={`{
|
<pre class="bg-gray-900 py-2 px-4 mb-2 block rounded-lg"><code class="language-json">{'{'}
|
||||||
"id": "paste id",
|
"id": "paste id",
|
||||||
"url": "https://0x45.st/some-random-id.md",
|
"url": "https://0x45.st/some-random-id.md",
|
||||||
"contents": "paste contents",
|
"contents": "paste contents",
|
||||||
|
@ -171,7 +203,7 @@
|
||||||
"encrypted": false,
|
"encrypted": false,
|
||||||
"burnAfterReading": false,
|
"burnAfterReading": false,
|
||||||
"created_at": "2021-08-05T07:30:00.000Z",
|
"created_at": "2021-08-05T07:30:00.000Z",
|
||||||
}`}></CodeBlock>
|
{'}'}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
<div class="fixed bottom-0 right-0 w-full md:w-auto">
|
||||||
|
|
|
@ -1,13 +1,111 @@
|
||||||
import { detectLanguage } from "$utils/hljs";
|
import { detectLanguage } from "$utils/hljs";
|
||||||
import { encrypt as doEncrypt } from "$utils/crypto";
|
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 { generate } from 'random-words';
|
||||||
import { Mongo } from "$lib/db/index";
|
import { Mongo } from "$lib/db/index";
|
||||||
import { env } from "$env/dynamic/private";
|
import { env } from "$env/dynamic/private";
|
||||||
import { extensionMap } from "$utils/languages";
|
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 }) => {
|
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: '-' });
|
const id = generate({ exactly: 3, join: '-' });
|
||||||
|
|
||||||
if (!contents || contents.length === 0) {
|
if (!contents || contents.length === 0) {
|
||||||
|
@ -28,9 +126,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
||||||
let pasteContents: string = contents;
|
let pasteContents: string = contents;
|
||||||
const highlight = language || detectLanguage(contents) || 'txt';
|
const highlight = language || detectLanguage(contents) || 'txt';
|
||||||
|
|
||||||
if (encrypt && !password) {
|
if (password) {
|
||||||
throw error(400, 'No password provided for encryption.');
|
|
||||||
} else if (encrypt) {
|
|
||||||
pasteContents = await doEncrypt(contents, password);
|
pasteContents = await doEncrypt(contents, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +134,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
||||||
id,
|
id,
|
||||||
url: `${env.SITE_URL}/${id}.${highlight}`,
|
url: `${env.SITE_URL}/${id}.${highlight}`,
|
||||||
highlight,
|
highlight,
|
||||||
encrypted: !!encrypt,
|
encrypted: !!password,
|
||||||
contents: pasteContents,
|
contents: pasteContents,
|
||||||
burnAfterReading: !!burnAfterReading,
|
burnAfterReading: !!burnAfterReading,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
@ -51,7 +147,11 @@ export const POST: RequestHandler = async ({ request }) => {
|
||||||
throw error(500, 'Failed to create paste');
|
throw error(500, 'Failed to create paste');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (raw) {
|
||||||
return json(data, {
|
return json(data, {
|
||||||
status: 201,
|
status: 201,
|
||||||
})
|
});
|
||||||
|
} else {
|
||||||
|
return text(data.url);
|
||||||
|
}
|
||||||
};
|
};
|
|
@ -53,6 +53,7 @@ function show_help {
|
||||||
|
|
||||||
burn=false
|
burn=false
|
||||||
encrypt=false
|
encrypt=false
|
||||||
|
raw=false
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
|
@ -134,7 +135,8 @@ json=$(jq -n \
|
||||||
--arg password "$password" \
|
--arg password "$password" \
|
||||||
--argjson encrypt $encrypt \
|
--argjson encrypt $encrypt \
|
||||||
--argjson burn $burn \
|
--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=$(
|
response=$(
|
||||||
curl -s -X POST $url \
|
curl -s -X POST $url \
|
||||||
|
@ -148,23 +150,19 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if there is an error
|
# Check if there is an error
|
||||||
error=$(echo $response | jq -r '.error')
|
error=$(echo "$response" | jq -r '.message' 2>/dev/null || true)
|
||||||
if [ "$error" != "null" ]; then
|
if [ -n "$error" ] && [ "$error" != "null" ]; then
|
||||||
echo "Error: $error"
|
echo "Error: $error"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If raw is set, return the raw JSON response
|
paste_url=$response
|
||||||
if [ ! -z "$raw" ]; then
|
if [ "$raw" = true ]; then
|
||||||
echo $response
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get the paste URL from the response
|
|
||||||
paste_url=$(echo $response | jq -r '.url')
|
paste_url=$(echo $response | jq -r '.url')
|
||||||
|
echo $response
|
||||||
# Print the paste URL
|
else
|
||||||
echo $paste_url
|
echo $paste_url
|
||||||
|
fi
|
||||||
|
|
||||||
# If copy is set, copy the paste URL to the clipboard
|
# If copy is set, copy the paste URL to the clipboard
|
||||||
if [ ! -z "$copy" ]; then
|
if [ ! -z "$copy" ]; then
|
||||||
|
|
|
@ -16,6 +16,10 @@ const config = {
|
||||||
alias: {
|
alias: {
|
||||||
'$db/*': './src/lib/db/*',
|
'$db/*': './src/lib/db/*',
|
||||||
'$utils/*': './src/utils/*',
|
'$utils/*': './src/utils/*',
|
||||||
|
},
|
||||||
|
|
||||||
|
csrf: {
|
||||||
|
checkOrigin: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue