Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
run: bun install

- name: Build Docs
run: bun nx build docs
run: bunx nx build docs

- name: Setup Pages
uses: actions/configure-pages@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
run: bun tsc

- name: Build and Test
run: bun nx affected --target=build,test
run: bunx nx affected --target=build,test

- name: Check for any generated code that should be committed
run: |
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,3 @@ app/frontend/cypress/screenshots
app/frontend/cypress/videos

.nx/

app/public/*
!app/public/island.svg
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
registry=https://registry.npmjs.org/
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM oven/bun:1.1.42
FROM oven/bun:1.2.0
WORKDIR /app

RUN useradd -ms /bin/sh admin
Expand Down
30 changes: 25 additions & 5 deletions action/dist/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,10 @@ var require_proxy = __commonJS({
})();
if (proxyVar) {
try {
return new URL(proxyVar);
return new DecodedURL(proxyVar);
} catch (_a) {
if (!proxyVar.startsWith("http://") && !proxyVar.startsWith("https://"))
return new URL(`http://${proxyVar}`);
return new DecodedURL(`http://${proxyVar}`);
}
} else {
return void 0;
Expand Down Expand Up @@ -282,6 +282,19 @@ var require_proxy = __commonJS({
const hostLower = host.toLowerCase();
return hostLower === "localhost" || hostLower.startsWith("127.") || hostLower.startsWith("[::1]") || hostLower.startsWith("[0:0:0:0:0:0:0:1]");
}
var DecodedURL = class extends URL {
constructor(url, base) {
super(url, base);
this._decodedUsername = decodeURIComponent(super.username);
this._decodedPassword = decodeURIComponent(super.password);
}
get username() {
return this._decodedUsername;
}
get password() {
return this._decodedPassword;
}
};
}
});

Expand Down Expand Up @@ -5267,6 +5280,13 @@ var require_body = __commonJS({
var { isUint8Array, isArrayBuffer } = require("util/types");
var { File: UndiciFile } = require_file();
var { parseMIMEType, serializeAMimeType } = require_dataURL();
var random;
try {
const crypto = require("crypto");
random = (max) => crypto.randomInt(0, max);
} catch {
random = (max) => Math.floor(Math.random(max));
}
var ReadableStream = globalThis.ReadableStream;
var File = NativeFile ?? UndiciFile;
var textEncoder = new TextEncoder();
Expand Down Expand Up @@ -5309,7 +5329,7 @@ var require_body = __commonJS({
} else if (ArrayBuffer.isView(object)) {
source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength));
} else if (util.isFormDataLike(object)) {
const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, "0")}`;
const boundary = `----formdata-undici-0${`${random(1e11)}`.padStart(11, "0")}`;
const prefix = `--${boundary}\r
Content-Disposition: form-data`;
const escape2 = (str) => str.replace(/\n/g, "%0A").replace(/\r/g, "%0D").replace(/"/g, "%22");
Expand Down Expand Up @@ -17825,7 +17845,7 @@ var require_lib = __commonJS({
}
const usingSsl = parsedUrl.protocol === "https:";
proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl.href, pipelining: !this._keepAlive ? 0 : 1 }, (proxyUrl.username || proxyUrl.password) && {
token: `${proxyUrl.username}:${proxyUrl.password}`
token: `Basic ${Buffer.from(`${proxyUrl.username}:${proxyUrl.password}`).toString("base64")}`
}));
this._proxyAgentDispatcher = proxyAgent;
if (usingSsl && this._ignoreSslError) {
Expand Down Expand Up @@ -31520,7 +31540,7 @@ var LRUCache = class _LRUCache {
}
/**
* Return an array of [key, {@link LRUCache.Entry}] tuples which can be
* passed to {@link LRLUCache#load}.
* passed to {@link LRUCache#load}.
*
* The `start` fields are calculated relative to a portable `Date.now()`
* timestamp, even if `performance.now()` is available.
Expand Down
2 changes: 1 addition & 1 deletion action/dist/main.js.map

Large diffs are not rendered by default.

21 changes: 1 addition & 20 deletions app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { PropsWithChildren } from 'react';
import React from 'react';
import { MainPage } from './frontend/components/main-page';
import { ClientProvider } from './frontend/providers/client-provider';
import { BaseImageStateProvider } from './frontend/providers/base-image-state-provider';
Expand Down Expand Up @@ -31,22 +31,3 @@ export function App(props: {
</ClientProvider>
);
}

export function OuterHtml(props: PropsWithChildren) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/public/island.svg" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
<link rel="stylesheet" href="/public/globals.css" />
<title>Comparadise</title>
</head>
<body>{props.children}</body>
</html>
);
}
12 changes: 6 additions & 6 deletions app/backend/src/getTemporaryObjectUrl.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { S3Client } from './s3Client';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { GetObjectCommand } from '@aws-sdk/client-s3';
import { s3 } from 'bun';

export const getTemporaryObjectUrl = async (key: string, bucket: string) => {
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
return getSignedUrl(S3Client, command, { expiresIn: oneHour });
export const getTemporaryObjectUrl = async (
filePath: string,
bucket: string
) => {
return s3.file(filePath, { bucket }).presign({ expiresIn: oneHour });
};

const oneHour = 3600;
51 changes: 1 addition & 50 deletions app/backend/src/router.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { AnyTRPCRouter, initTRPC } from '@trpc/server';
import {
type FetchHandlerRequestOptions,
fetchRequestHandler
} from '@trpc/server/adapters/fetch';
import { initTRPC } from '@trpc/server';
import {
fetchCurrentPageInputSchema,
updateBaseImagesInputSchema
} from './schema';
import { fetchCurrentPage } from './fetchCurrentPage';
import { updateBaseImagesInS3 } from './updateBaseImagesInS3';
import Elysia from 'elysia';

const t = initTRPC.create();

Expand All @@ -23,47 +18,3 @@ export const router = t.router({
});

export type AppRouter = typeof router;

type TRPCOptions = {
endpoint?: string;
} & Omit<
FetchHandlerRequestOptions<AnyTRPCRouter>,
'req' | 'router' | 'endpoint'
>;

export const trpcRouter =
(
router: AnyTRPCRouter,
{ endpoint = '/trpc', ...options }: TRPCOptions = { endpoint: '/trpc' }
) =>
(app: Elysia) => {
return app
.onParse(({ request: { url } }) => {
if (getPath(url).startsWith(endpoint)) return true;
})
.get(`${endpoint}/*`, async ({ request }) => {
return fetchRequestHandler({
...options,
req: request,
router,
endpoint
});
})
.post(`${endpoint}/*`, async ({ request }) => {
return fetchRequestHandler({
...options,
req: request,
router,
endpoint
});
});
};

const getPath = (url: string) => {
const start = url.indexOf('/', 9);
const end = url.indexOf('?', start);

if (end === -1) return url.slice(start);

return url.slice(start, end);
};
6 changes: 3 additions & 3 deletions app/backend/test/fetchCurrentPage.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { fetchCurrentPage } from '../src/fetchCurrentPage';
import { getTemporaryObjectUrl } from '../src/getTemporaryObjectUrl';
import { getGroupedKeys } from '../src/getGroupedKeys';
import { expect } from '@jest/globals';

jest.mock('../src/getGroupedKeys');
jest.mock('../src/getTemporaryObjectUrl');
jest.mock('../src/getTemporaryObjectUrl', () => ({
getTemporaryObjectUrl: jest.fn(() => 'url')
}));

(getGroupedKeys as jest.Mock).mockResolvedValue([
{
Expand All @@ -20,7 +21,6 @@ jest.mock('../src/getTemporaryObjectUrl');
keys: ['hash/EXTRA_LARGE/pdpPage/new.png']
}
]);
(getTemporaryObjectUrl as jest.Mock).mockResolvedValue('url');

describe('fetchCurrentPage', () => {
it('should get first page of images', async () => {
Expand Down
21 changes: 10 additions & 11 deletions app/client.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import { App } from './app';

import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { App, OuterHtml } from './app';

hydrateRoot(
document,
<BrowserRouter>
<OuterHtml>
document.addEventListener('DOMContentLoaded', () => {
const root = createRoot(document.getElementById('root')!);
root.render(
<BrowserRouter>
<App />
</OuterHtml>
</BrowserRouter>
);
</BrowserRouter>
);
});
7 changes: 4 additions & 3 deletions app/frontend/cypress/component/App.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ import {
onlyNewImagesSecondPage,
secondPage
} from '../mocks/pages';
import { CyHttpMessages } from 'cypress/types/net-stubbing';
import {
baseImageUpdateRejection,
MOCK_ERROR_MESSAGE
} from '../mocks/base-image-update-rejection';
import { mutationResponse } from '../mocks/mutation';
import { MemoryRouter } from 'react-router-dom';

const getPageFromRequest = (req: CyHttpMessages.IncomingHttpRequest) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getPageFromRequest(req: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(JSON.parse(req.query.input as string) as any)['0'].page;
return (JSON.parse(req.query.input as string) as any)['0'].page;
}

describe('App', () => {
describe('homepage', () => {
Expand Down
2 changes: 1 addition & 1 deletion app/frontend/cypress/mocks/mutation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TRPCResponse } from '@trpc/server/dist/rpc';
import { TRPCResponse } from '@trpc/server/rpc';

export const mutationResponse: TRPCResponse = {
result: {
Expand Down
15 changes: 6 additions & 9 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,32 @@
"private": true,
"dependencies": {
"@aws-sdk/client-s3": "3.717.0",
"@aws-sdk/s3-request-presigner": "3.717.0",
"@elysiajs/static": "1.1.2",
"@headlessui/react": "2.2.0",
"@octokit/rest": "20.1.1",
"@tanstack/react-query": "5.62.8",
"@tanstack/react-query": "5.62.11",
"@trpc/client": "next",
"@trpc/react-query": "next",
"@trpc/server": "next",
"@types/lodash": "4.17.13",
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"elysia": "1.1.26",
"lodash": "4.17.21",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-router-dom": "7.1.0",
"react-router-dom": "7.1.1",
"trpc-bun-adapter": "1.2.2",
"tailwindcss": "3.4.17",
"zod": "3.24.1"
},
"devDependencies": {
"@testing-library/cypress": "10.0.2",
"@types/testing-library__cypress": "5.0.13",
"prettier-plugin-tailwindcss": "0.6.9",
"vite": "6.0.5"
"vite": "6.0.9"
},
"scripts": {
"dev": "NODE_ENV=development bun --hot ./server.tsx",
"prestart": "tailwindcss -o ./public/globals.css",
"start": "bun ./server.tsx",
"dev": "NODE_ENV=development bun --hot ./server.ts",
"start": "bun ./server.ts",
"test": "nx docker comparadise && nx test:e2e frontend"
}
}
17 changes: 17 additions & 0 deletions app/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html lang="en">
<head>
<title>Comparadise</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="./island.svg" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div id="root"></div>
<script src="../client.tsx" type="module"></script>
</body>
</html>
21 changes: 21 additions & 0 deletions app/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { router } from './backend/src/router';
import { createBunHttpHandler } from 'trpc-bun-adapter';
import { serve } from 'bun';
import index from './public/index.html';

const server = serve({
static: {
'/': index
},
port: process.env.PORT ?? 8080,
async fetch(request, response) {
const trpcHandler = createBunHttpHandler({ router, endpoint: '/trpc' });
return (
trpcHandler(request, response) ??
new Response('Not found', { status: 404 })
);
},
development: true
});

console.log(`Server running at ${server.url}`);
Loading
Loading