commit a66a344cc00e7bbb93b82f5350da91b6c38c16a0 Author: yourfriendoss Date: Thu Jul 10 18:53:12 2025 +0300 new commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51f1bf1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store +cache/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..98ecdfd --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# sanjuuni-api + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.2.13. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..30b3b4f --- /dev/null +++ b/bun.lock @@ -0,0 +1,37 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "sanjuuni-api", + "dependencies": { + "emoji-toolkit": "^9.0.1", + "hono": "^4.8.4", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], + + "@types/node": ["@types/node@24.0.12", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="], + + "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], + + "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "emoji-toolkit": ["emoji-toolkit@9.0.1", "", {}, "sha512-sMMNqKNLVHXJfIKoPbrRJwtYuysVNC9GlKetr72zE3SSVbHqoeDLWVrxP0uM0AE0qvdl3hbUk+tJhhwXZrDHaw=="], + + "hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..75ef6b4 --- /dev/null +++ b/index.ts @@ -0,0 +1,68 @@ +import { Hono } from 'hono' +const app = new Hono() +const joypixels = require("emoji-toolkit"); +import { mkdirSync, writeFileSync, existsSync, exists, readFileSync } from 'fs' +import { $ } from 'bun'; + + +app.post('/render', async (c) => { + const auth = c.req.header('Authorization') + if (auth !== process.env.PASSWORD) { + return c.text('Unauthorized', 401) + } + + const body = await c.req.json().catch(() => null) + if (!body?.emoji) { + return c.text('Missing emoji in body', 400) + } + + let extraArgs = "-b" + + if(body?.extraArgs) { + if (typeof body.extraArgs !== 'string') { + return c.text('extraArgs must be a string', 400) + } + extraArgs = body.extraArgs; + } + + const emojiHtml = joypixels.toImage(body.emoji) + const urlMatch = emojiHtml.match(/src="([^"]*)"/) + if (!urlMatch) { + return c.text('Could not extract emoji URL', 500) + } + const emojiUrl = urlMatch[1] + + const filename = "cache/" + emojiUrl.split('/').pop() ; + if(!existsSync(filename)) { + + // Download the image + const res = await fetch(emojiUrl) + if (!res.ok) { + return c.text('Failed to download emoji image', 500) + } + const arrayBuffer = await res.arrayBuffer() + const buffer = Buffer.from(arrayBuffer) + + // Ensure cache directory exists + if (!existsSync('cache')) { + mkdirSync('cache') + } + + // Get filename from URL + writeFileSync(filename, buffer) + } + + if(!existsSync(filename+".bimg")) { + await $`sanjuuni --disable-opencl ${extraArgs} -i ${filename} -o ${filename+".bimg"}` + } + + const bimg = readFileSync(filename + ".bimg", "utf8"); + + return c.text(bimg) +}) + +export default { + port: 7427, + host: "127.0.0.1", + fetch: app.fetch, +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ed3e714 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "sanjuuni-api", + "module": "index.ts", + "type": "module", + "private": true, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "emoji-toolkit": "^9.0.1", + "hono": "^4.8.4" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9c62f74 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}