Compare commits
12 Commits
2b21ffd170
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c84a583992 | |||
| ea12e01eed | |||
| e0a74eeb9e | |||
| 35043fc9e3 | |||
| 42ceb798d1 | |||
| 5cf8b26737 | |||
| a85950d6ef | |||
| d3bb5c2be1 | |||
| c068e243b0 | |||
| 81ae2650b2 | |||
| 5519c016aa | |||
| 30a52c800a |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "proto"]
|
||||
path = proto
|
||||
url = https://git.kocoder.xyz/vt/proto
|
||||
18
buf.gen.yaml
Normal file
18
buf.gen.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
version: v2
|
||||
plugins:
|
||||
- remote: buf.build/bufbuild/es:v2.12.0
|
||||
out: src/gen
|
||||
opt: target=ts
|
||||
include_imports: true
|
||||
- remote: buf.build/connectrpc/query-es:v2.2.0
|
||||
out: src/gen
|
||||
opt:
|
||||
- target=ts
|
||||
managed:
|
||||
enabled: true
|
||||
override:
|
||||
- file_option: go_package_prefix
|
||||
value: git.kocoder.xyz/vt/shortener/internal
|
||||
disable:
|
||||
- file_option: go_package
|
||||
module: buf.build/bufbuild/protovalidate
|
||||
6
buf.lock
Normal file
6
buf.lock
Normal file
@@ -0,0 +1,6 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v2
|
||||
deps:
|
||||
- name: buf.build/bufbuild/protovalidate
|
||||
commit: 50325440f8f24053b047484a6bf60b76
|
||||
digest: b5:74cb6f5c0853c3c10aafc701614194bbd63326bdb8ef4068214454b8894b03ba4113e04b3a33a8321cdf05336e37db4dc14a5e2495db8462566914f36086ba31
|
||||
9
buf.yaml
Normal file
9
buf.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: v2
|
||||
deps:
|
||||
- buf.build/bufbuild/protovalidate
|
||||
lint:
|
||||
use:
|
||||
- STANDARD
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
||||
@@ -13,6 +13,11 @@
|
||||
"check": "biome check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.12.0",
|
||||
"@bufbuild/protovalidate": "^1.2.0",
|
||||
"@connectrpc/connect": "^2.1.1",
|
||||
"@connectrpc/connect-query": "^2.2.0",
|
||||
"@connectrpc/connect-web": "^2.1.1",
|
||||
"@fontsource-variable/noto-sans": "^5.2.10",
|
||||
"@fontsource/inter": "^5.1.1",
|
||||
"@kobalte/core": "^0.13.11",
|
||||
@@ -33,7 +38,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^1.16.0",
|
||||
"nitro": "npm:nitro-nightly@latest",
|
||||
"nitro": "3.0.260429-beta",
|
||||
"radix-ui": "^1.4.3",
|
||||
"shadcn": "^4.7.0",
|
||||
"solid-js": "^1.9.12",
|
||||
@@ -46,6 +51,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.4.5",
|
||||
"@bufbuild/buf": "^1.70.0",
|
||||
"@solidjs/testing-library": "^0.8.10",
|
||||
"@tanstack/devtools-vite": "latest",
|
||||
"jsdom": "^28.1.0",
|
||||
|
||||
629
pnpm-lock.yaml
generated
629
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
allowBuilds:
|
||||
'@bufbuild/buf': true
|
||||
esbuild: true
|
||||
msw: true
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
|
||||
1
proto
Submodule
1
proto
Submodule
Submodule proto added at 382266dac5
@@ -1,13 +1,14 @@
|
||||
import { Link } from '@tanstack/solid-router'
|
||||
import { Link } from "@tanstack/solid-router";
|
||||
|
||||
import TanStackQueryHeaderUser from '../integrations/tanstack-query/header-user.tsx'
|
||||
import TanStackQueryHeaderUser from "../integrations/tanstack-query/header-user.tsx";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<div class="flex items-center justify-between px-4">
|
||||
<Link to="/">Home</Link>
|
||||
<Link to="/todo">Todo</Link>
|
||||
<Link to="/shorten">Shorten</Link>
|
||||
<TanStackQueryHeaderUser />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
79
src/components/shortener/createRedirectionForm.tsx
Normal file
79
src/components/shortener/createRedirectionForm.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
useConnectMutation,
|
||||
getConnectQueryKey,
|
||||
} from "~/integrations/connect-query/solid";
|
||||
import {
|
||||
createURLRedirection,
|
||||
listURLRedirections,
|
||||
} from "~/gen/proto/shorten/v1/shorten-ShortenService_connectquery";
|
||||
import useAppForm from "../form/appform";
|
||||
import { Button } from "../ui/button";
|
||||
import { useQueryClient } from "@tanstack/solid-query";
|
||||
|
||||
const CreateRedirectionForm = () => {
|
||||
const mutation = useConnectMutation(createURLRedirection);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const form = useAppForm(() => ({
|
||||
defaultValues: {
|
||||
url: "",
|
||||
slug: "",
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
await mutation.mutateAsync({
|
||||
url: values.value.url,
|
||||
shortCode: values.value.slug,
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getConnectQueryKey(listURLRedirections),
|
||||
});
|
||||
form.reset();
|
||||
},
|
||||
}));
|
||||
return (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<div class="bg-card p-2">
|
||||
<h2 class="p-2 my-2">Create New Redirection</h2>
|
||||
<div class="flex gap-x-2 p-2">
|
||||
<div class="w-1/2">
|
||||
<form.AppField
|
||||
name="slug"
|
||||
children={(field) => (
|
||||
<field.TextField
|
||||
name="slug"
|
||||
label="Slug"
|
||||
description={"The slug which should redirect"}
|
||||
placeholder="https://google.com"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<form.AppField
|
||||
name="url"
|
||||
children={(field) => (
|
||||
<field.TextField
|
||||
name="url"
|
||||
label="URL"
|
||||
description={"The URL to redirect to"}
|
||||
placeholder="https://google.com"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" class="my-auto">
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateRedirectionForm;
|
||||
73
src/components/shortener/listRedirections.tsx
Normal file
73
src/components/shortener/listRedirections.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { For, Match, Switch } from "solid-js";
|
||||
import { listURLRedirections } from "~/gen/proto/shorten/v1/shorten-ShortenService_connectquery";
|
||||
import type { URLRedirection } from "~/gen/proto/shorten/v1/shorten_pb";
|
||||
import { useConnectQuery } from "~/integrations/connect-query/solid";
|
||||
|
||||
const ListRedirections = () => {
|
||||
const query = useConnectQuery(listURLRedirections);
|
||||
|
||||
return (
|
||||
<div class="bg-card p-4">
|
||||
<h2 class="p-2 my-2">List of Redirections</h2>
|
||||
<Switch fallback={<div>Loading...</div>}>
|
||||
<Match when={query.isError}>
|
||||
<div>Error: {query.error?.message}</div>
|
||||
</Match>
|
||||
<Match when={query.isLoading}>
|
||||
<div>Loading...</div>
|
||||
</Match>
|
||||
<Match when={query.data}>
|
||||
<table class="border w-full border-collapse p-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="border p-2">Short Code</td>
|
||||
<td class="border p-2">URL</td>
|
||||
<td class="border p-2">Active</td>
|
||||
<td class="border p-2">Created At</td>
|
||||
<td class="border p-2">Expires At</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<For each={query.data?.urlRedirections}>
|
||||
{(item) => <Redirection item={item} />}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Redirection = (props: { item: URLRedirection }) => {
|
||||
return (
|
||||
<tr>
|
||||
<td class="border p-2">
|
||||
<a
|
||||
href={`https://l.kocoder.xyz/${props.item.shortCode}`}
|
||||
target="_blank"
|
||||
>
|
||||
{props.item.shortCode}
|
||||
</a>
|
||||
</td>
|
||||
<td class="border p-2">{props.item.url}</td>
|
||||
<td class="border p-2">{props.item.isActive ? "Yes" : "No"}</td>
|
||||
<td class="border p-2">
|
||||
{props.item.createdAt
|
||||
? new Date(
|
||||
Number(props.item.createdAt.seconds) * 1000,
|
||||
).toLocaleDateString()
|
||||
: ""}
|
||||
</td>
|
||||
<td class="border p-2">
|
||||
{props.item.createdAt
|
||||
? new Date(
|
||||
Number(props.item.expiresAt.seconds) * 1000,
|
||||
).toLocaleDateString()
|
||||
: ""}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListRedirections;
|
||||
5028
src/gen/buf/validate/validate_pb.ts
Normal file
5028
src/gen/buf/validate/validate_pb.ts
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
|
||||
// @generated by protoc-gen-connect-query v2.2.0 with parameter "target=ts"
|
||||
// @generated from file proto/shorten/v1/shorten.proto (package proto.shorten.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
import { ShortenService } from "./shorten_pb";
|
||||
|
||||
/**
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.CreateURLRedirection
|
||||
*/
|
||||
export const createURLRedirection = ShortenService.method.createURLRedirection;
|
||||
|
||||
/**
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.ListURLRedirections
|
||||
*/
|
||||
export const listURLRedirections = ShortenService.method.listURLRedirections;
|
||||
|
||||
/**
|
||||
* rpc GetURLRedirection(GetURLRedirectionRequest) returns
|
||||
* (GetURLRedirectionResponse) {}
|
||||
*
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.DeactivateURLRedirection
|
||||
*/
|
||||
export const deactivateURLRedirection = ShortenService.method.deactivateURLRedirection;
|
||||
|
||||
/**
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.DeleteURLRedirection
|
||||
*/
|
||||
export const deleteURLRedirection = ShortenService.method.deleteURLRedirection;
|
||||
248
src/gen/proto/shorten/v1/shorten_pb.ts
Normal file
248
src/gen/proto/shorten/v1/shorten_pb.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
// @generated by protoc-gen-es v2.12.0 with parameter "target=ts"
|
||||
// @generated from file proto/shorten/v1/shorten.proto (package proto.shorten.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv2";
|
||||
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv2";
|
||||
import { file_buf_validate_validate } from "../../../buf/validate/validate_pb";
|
||||
import type { Timestamp } from "@bufbuild/protobuf/wkt";
|
||||
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
|
||||
import type { Message } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* Describes the file proto/shorten/v1/shorten.proto.
|
||||
*/
|
||||
export const file_proto_shorten_v1_shorten: GenFile = /*@__PURE__*/
|
||||
fileDesc("Ch5wcm90by9zaG9ydGVuL3YxL3Nob3J0ZW4ucHJvdG8SEHByb3RvLnNob3J0ZW4udjEiUgobQ3JlYXRlVVJMUmVkaXJlY3Rpb25SZXF1ZXN0EhwKCnNob3J0X2NvZGUYASABKAlCCLpIBXIDmAEKEhUKA3VybBgCIAEoCUIIukgFcgOIAQEiKgocQ3JlYXRlVVJMUmVkaXJlY3Rpb25SZXNwb25zZRIKCgJvaxgBIAEoCCLQAQoOVVJMUmVkaXJlY3Rpb24SDgoGdXJsX2lkGAEgASgFEhIKCnNob3J0X2NvZGUYAyABKAkSCwoDdXJsGAIgASgJEi4KCmNyZWF0ZWRfYXQYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCmV4cGlyZXNfYXQYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhEKCWlzX2FjdGl2ZRgGIAEoCBIaChJjbGlja19jb3VudF9ieV9kYXkYByADKAUiHAoaTGlzdFVSTFJlZGlyZWN0aW9uc1JlcXVlc3QiWQobTGlzdFVSTFJlZGlyZWN0aW9uc1Jlc3BvbnNlEjoKEHVybF9yZWRpcmVjdGlvbnMYASADKAsyIC5wcm90by5zaG9ydGVuLnYxLlVSTFJlZGlyZWN0aW9uIkQKH0RlYWN0aXZhdGVVUkxSZWRpcmVjdGlvblJlcXVlc3QSDgoGdXJsX2lkGAEgASgFEhEKCWlzX2FjdGl2ZRgCIAEoCCIuCiBEZWFjdGl2YXRlVVJMUmVkaXJlY3Rpb25SZXNwb25zZRIKCgJvaxgBIAEoCCItChtEZWxldGVVUkxSZWRpcmVjdGlvblJlcXVlc3QSDgoGdXJsX2lkGAEgASgFIioKHERlbGV0ZVVSTFJlZGlyZWN0aW9uUmVzcG9uc2USCgoCb2sYASABKAgy/gMKDlNob3J0ZW5TZXJ2aWNlEncKFENyZWF0ZVVSTFJlZGlyZWN0aW9uEi0ucHJvdG8uc2hvcnRlbi52MS5DcmVhdGVVUkxSZWRpcmVjdGlvblJlcXVlc3QaLi5wcm90by5zaG9ydGVuLnYxLkNyZWF0ZVVSTFJlZGlyZWN0aW9uUmVzcG9uc2UiABJ0ChNMaXN0VVJMUmVkaXJlY3Rpb25zEiwucHJvdG8uc2hvcnRlbi52MS5MaXN0VVJMUmVkaXJlY3Rpb25zUmVxdWVzdBotLnByb3RvLnNob3J0ZW4udjEuTGlzdFVSTFJlZGlyZWN0aW9uc1Jlc3BvbnNlIgASgwEKGERlYWN0aXZhdGVVUkxSZWRpcmVjdGlvbhIxLnByb3RvLnNob3J0ZW4udjEuRGVhY3RpdmF0ZVVSTFJlZGlyZWN0aW9uUmVxdWVzdBoyLnByb3RvLnNob3J0ZW4udjEuRGVhY3RpdmF0ZVVSTFJlZGlyZWN0aW9uUmVzcG9uc2UiABJ3ChREZWxldGVVUkxSZWRpcmVjdGlvbhItLnByb3RvLnNob3J0ZW4udjEuRGVsZXRlVVJMUmVkaXJlY3Rpb25SZXF1ZXN0Gi4ucHJvdG8uc2hvcnRlbi52MS5EZWxldGVVUkxSZWRpcmVjdGlvblJlc3BvbnNlIgBCyAEKFGNvbS5wcm90by5zaG9ydGVuLnYxQgxTaG9ydGVuUHJvdG9QAVpAZ2l0LmtvY29kZXIueHl6L3Z0L3Nob3J0ZW5lci9pbnRlcm5hbC9wcm90by9zaG9ydGVuL3YxO3Nob3J0ZW52MaICA1BTWKoCEFByb3RvLlNob3J0ZW4uVjHKAhBQcm90b1xTaG9ydGVuXFYx4gIcUHJvdG9cU2hvcnRlblxWMVxHUEJNZXRhZGF0YeoCElByb3RvOjpTaG9ydGVuOjpWMWIGcHJvdG8z", [file_buf_validate_validate, file_google_protobuf_timestamp]);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.CreateURLRedirectionRequest
|
||||
*/
|
||||
export type CreateURLRedirectionRequest = Message<"proto.shorten.v1.CreateURLRedirectionRequest"> & {
|
||||
/**
|
||||
* @generated from field: string short_code = 1;
|
||||
*/
|
||||
shortCode: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string url = 2;
|
||||
*/
|
||||
url: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.CreateURLRedirectionRequest.
|
||||
* Use `create(CreateURLRedirectionRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const CreateURLRedirectionRequestSchema: GenMessage<CreateURLRedirectionRequest> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 0);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.CreateURLRedirectionResponse
|
||||
*/
|
||||
export type CreateURLRedirectionResponse = Message<"proto.shorten.v1.CreateURLRedirectionResponse"> & {
|
||||
/**
|
||||
* @generated from field: bool ok = 1;
|
||||
*/
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.CreateURLRedirectionResponse.
|
||||
* Use `create(CreateURLRedirectionResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const CreateURLRedirectionResponseSchema: GenMessage<CreateURLRedirectionResponse> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 1);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.URLRedirection
|
||||
*/
|
||||
export type URLRedirection = Message<"proto.shorten.v1.URLRedirection"> & {
|
||||
/**
|
||||
* @generated from field: int32 url_id = 1;
|
||||
*/
|
||||
urlId: number;
|
||||
|
||||
/**
|
||||
* @generated from field: string short_code = 3;
|
||||
*/
|
||||
shortCode: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string url = 2;
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* @generated from field: google.protobuf.Timestamp created_at = 4;
|
||||
*/
|
||||
createdAt?: Timestamp | undefined;
|
||||
|
||||
/**
|
||||
* @generated from field: google.protobuf.Timestamp expires_at = 5;
|
||||
*/
|
||||
expiresAt?: Timestamp | undefined;
|
||||
|
||||
/**
|
||||
* @generated from field: bool is_active = 6;
|
||||
*/
|
||||
isActive: boolean;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated int32 click_count_by_day = 7;
|
||||
*/
|
||||
clickCountByDay: number[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.URLRedirection.
|
||||
* Use `create(URLRedirectionSchema)` to create a new message.
|
||||
*/
|
||||
export const URLRedirectionSchema: GenMessage<URLRedirection> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 2);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.ListURLRedirectionsRequest
|
||||
*/
|
||||
export type ListURLRedirectionsRequest = Message<"proto.shorten.v1.ListURLRedirectionsRequest"> & {
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.ListURLRedirectionsRequest.
|
||||
* Use `create(ListURLRedirectionsRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const ListURLRedirectionsRequestSchema: GenMessage<ListURLRedirectionsRequest> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 3);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.ListURLRedirectionsResponse
|
||||
*/
|
||||
export type ListURLRedirectionsResponse = Message<"proto.shorten.v1.ListURLRedirectionsResponse"> & {
|
||||
/**
|
||||
* @generated from field: repeated proto.shorten.v1.URLRedirection url_redirections = 1;
|
||||
*/
|
||||
urlRedirections: URLRedirection[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.ListURLRedirectionsResponse.
|
||||
* Use `create(ListURLRedirectionsResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const ListURLRedirectionsResponseSchema: GenMessage<ListURLRedirectionsResponse> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 4);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.DeactivateURLRedirectionRequest
|
||||
*/
|
||||
export type DeactivateURLRedirectionRequest = Message<"proto.shorten.v1.DeactivateURLRedirectionRequest"> & {
|
||||
/**
|
||||
* @generated from field: int32 url_id = 1;
|
||||
*/
|
||||
urlId: number;
|
||||
|
||||
/**
|
||||
* @generated from field: bool is_active = 2;
|
||||
*/
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.DeactivateURLRedirectionRequest.
|
||||
* Use `create(DeactivateURLRedirectionRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const DeactivateURLRedirectionRequestSchema: GenMessage<DeactivateURLRedirectionRequest> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 5);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.DeactivateURLRedirectionResponse
|
||||
*/
|
||||
export type DeactivateURLRedirectionResponse = Message<"proto.shorten.v1.DeactivateURLRedirectionResponse"> & {
|
||||
/**
|
||||
* @generated from field: bool ok = 1;
|
||||
*/
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.DeactivateURLRedirectionResponse.
|
||||
* Use `create(DeactivateURLRedirectionResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const DeactivateURLRedirectionResponseSchema: GenMessage<DeactivateURLRedirectionResponse> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 6);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.DeleteURLRedirectionRequest
|
||||
*/
|
||||
export type DeleteURLRedirectionRequest = Message<"proto.shorten.v1.DeleteURLRedirectionRequest"> & {
|
||||
/**
|
||||
* @generated from field: int32 url_id = 1;
|
||||
*/
|
||||
urlId: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.DeleteURLRedirectionRequest.
|
||||
* Use `create(DeleteURLRedirectionRequestSchema)` to create a new message.
|
||||
*/
|
||||
export const DeleteURLRedirectionRequestSchema: GenMessage<DeleteURLRedirectionRequest> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 7);
|
||||
|
||||
/**
|
||||
* @generated from message proto.shorten.v1.DeleteURLRedirectionResponse
|
||||
*/
|
||||
export type DeleteURLRedirectionResponse = Message<"proto.shorten.v1.DeleteURLRedirectionResponse"> & {
|
||||
/**
|
||||
* @generated from field: bool ok = 1;
|
||||
*/
|
||||
ok: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message proto.shorten.v1.DeleteURLRedirectionResponse.
|
||||
* Use `create(DeleteURLRedirectionResponseSchema)` to create a new message.
|
||||
*/
|
||||
export const DeleteURLRedirectionResponseSchema: GenMessage<DeleteURLRedirectionResponse> = /*@__PURE__*/
|
||||
messageDesc(file_proto_shorten_v1_shorten, 8);
|
||||
|
||||
/**
|
||||
* @generated from service proto.shorten.v1.ShortenService
|
||||
*/
|
||||
export const ShortenService: GenService<{
|
||||
/**
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.CreateURLRedirection
|
||||
*/
|
||||
createURLRedirection: {
|
||||
methodKind: "unary";
|
||||
input: typeof CreateURLRedirectionRequestSchema;
|
||||
output: typeof CreateURLRedirectionResponseSchema;
|
||||
},
|
||||
/**
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.ListURLRedirections
|
||||
*/
|
||||
listURLRedirections: {
|
||||
methodKind: "unary";
|
||||
input: typeof ListURLRedirectionsRequestSchema;
|
||||
output: typeof ListURLRedirectionsResponseSchema;
|
||||
},
|
||||
/**
|
||||
* rpc GetURLRedirection(GetURLRedirectionRequest) returns
|
||||
* (GetURLRedirectionResponse) {}
|
||||
*
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.DeactivateURLRedirection
|
||||
*/
|
||||
deactivateURLRedirection: {
|
||||
methodKind: "unary";
|
||||
input: typeof DeactivateURLRedirectionRequestSchema;
|
||||
output: typeof DeactivateURLRedirectionResponseSchema;
|
||||
},
|
||||
/**
|
||||
* @generated from rpc proto.shorten.v1.ShortenService.DeleteURLRedirection
|
||||
*/
|
||||
deleteURLRedirection: {
|
||||
methodKind: "unary";
|
||||
input: typeof DeleteURLRedirectionRequestSchema;
|
||||
output: typeof DeleteURLRedirectionResponseSchema;
|
||||
},
|
||||
}> = /*@__PURE__*/
|
||||
serviceDesc(file_proto_shorten_v1_shorten, 0);
|
||||
|
||||
107
src/integrations/connect-query/solid.tsx
Normal file
107
src/integrations/connect-query/solid.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { createContext, useContext } from "solid-js";
|
||||
import type { JSX } from "solid-js";
|
||||
import type { Transport, ConnectError } from "@connectrpc/connect";
|
||||
import { createQuery, createMutation } from "@tanstack/solid-query";
|
||||
import type {
|
||||
CreateQueryResult,
|
||||
CreateQueryOptions,
|
||||
CreateMutationResult,
|
||||
CreateMutationOptions,
|
||||
} from "@tanstack/solid-query";
|
||||
import { createQueryOptions, callUnaryMethod } from "@connectrpc/connect-query";
|
||||
import type {
|
||||
DescMethodUnary,
|
||||
DescMessage,
|
||||
MessageInitShape,
|
||||
MessageShape,
|
||||
} from "@bufbuild/protobuf";
|
||||
|
||||
const TransportContext = createContext<Transport>();
|
||||
|
||||
export interface TransportProviderProps {
|
||||
transport: Transport;
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
export function TransportProvider(props: TransportProviderProps) {
|
||||
console.log("TransportProvider rendering, transport:", props.transport);
|
||||
return (
|
||||
<TransportContext.Provider value={props.transport}>
|
||||
{props.children}
|
||||
</TransportContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTransport() {
|
||||
const transport = useContext(TransportContext);
|
||||
console.log("useTransport called, received:", transport);
|
||||
if (!transport) {
|
||||
throw new Error("useTransport must be used within a TransportProvider");
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
|
||||
export function useConnectQuery<I extends DescMessage, O extends DescMessage>(
|
||||
schema: DescMethodUnary<I, O>,
|
||||
input?: MessageInitShape<I>,
|
||||
options?: Omit<
|
||||
CreateQueryOptions<MessageShape<O>, ConnectError>,
|
||||
"queryKey" | "queryFn"
|
||||
> & {
|
||||
transport?: Transport;
|
||||
},
|
||||
): CreateQueryResult<MessageShape<O>, ConnectError> {
|
||||
const contextTransport = useTransport();
|
||||
|
||||
return createQuery(() => {
|
||||
const transport = options?.transport ?? contextTransport;
|
||||
const baseOptions = createQueryOptions(schema, input, { transport });
|
||||
return {
|
||||
...baseOptions,
|
||||
...options,
|
||||
} as any;
|
||||
});
|
||||
}
|
||||
|
||||
export function useConnectMutation<
|
||||
I extends DescMessage,
|
||||
O extends DescMessage,
|
||||
Ctx = unknown
|
||||
>(
|
||||
schema: DescMethodUnary<I, O>,
|
||||
options?: Omit<
|
||||
CreateMutationOptions<MessageShape<O>, ConnectError, MessageInitShape<I>, Ctx>,
|
||||
"mutationFn"
|
||||
> & {
|
||||
transport?: Transport;
|
||||
},
|
||||
): CreateMutationResult<MessageShape<O>, ConnectError, MessageInitShape<I>, Ctx> {
|
||||
const contextTransport = useTransport();
|
||||
|
||||
return createMutation(() => {
|
||||
const transport = options?.transport ?? contextTransport;
|
||||
return {
|
||||
...options,
|
||||
mutationFn: async (input: MessageInitShape<I>) => {
|
||||
return callUnaryMethod(transport, schema, input);
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function getConnectQueryKey<
|
||||
I extends DescMessage,
|
||||
O extends DescMessage
|
||||
>(
|
||||
schema: DescMethodUnary<I, O>,
|
||||
input?: MessageInitShape<I>,
|
||||
): [string, { serviceName: string; methodName: string; input?: MessageInitShape<I> }] {
|
||||
return [
|
||||
"connect-query",
|
||||
{
|
||||
serviceName: schema.parent.typeName,
|
||||
methodName: schema.name,
|
||||
...(input !== undefined ? { input } : {}),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import { QueryClient } from '@tanstack/solid-query'
|
||||
import { QueryClient } from "@tanstack/solid-query";
|
||||
import { createConnectTransport } from "@connectrpc/connect-web";
|
||||
|
||||
export function getContext() {
|
||||
const queryClient = new QueryClient()
|
||||
const queryClient = new QueryClient();
|
||||
const transport = createConnectTransport({
|
||||
baseUrl: "https://l.kocoder.xyz",
|
||||
});
|
||||
|
||||
return {
|
||||
queryClient,
|
||||
}
|
||||
transport,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as TodoRouteImport } from './routes/todo'
|
||||
import { Route as ShortenRouteImport } from './routes/shorten'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
|
||||
const TodoRoute = TodoRouteImport.update({
|
||||
@@ -17,6 +18,11 @@ const TodoRoute = TodoRouteImport.update({
|
||||
path: '/todo',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const ShortenRoute = ShortenRouteImport.update({
|
||||
id: '/shorten',
|
||||
path: '/shorten',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const IndexRoute = IndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
@@ -25,27 +31,31 @@ const IndexRoute = IndexRouteImport.update({
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/shorten': typeof ShortenRoute
|
||||
'/todo': typeof TodoRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/shorten': typeof ShortenRoute
|
||||
'/todo': typeof TodoRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/shorten': typeof ShortenRoute
|
||||
'/todo': typeof TodoRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/' | '/todo'
|
||||
fullPaths: '/' | '/shorten' | '/todo'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/' | '/todo'
|
||||
id: '__root__' | '/' | '/todo'
|
||||
to: '/' | '/shorten' | '/todo'
|
||||
id: '__root__' | '/' | '/shorten' | '/todo'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
ShortenRoute: typeof ShortenRoute
|
||||
TodoRoute: typeof TodoRoute
|
||||
}
|
||||
|
||||
@@ -58,6 +68,13 @@ declare module '@tanstack/solid-router' {
|
||||
preLoaderRoute: typeof TodoRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/shorten': {
|
||||
id: '/shorten'
|
||||
path: '/shorten'
|
||||
fullPath: '/shorten'
|
||||
preLoaderRoute: typeof ShortenRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
@@ -70,6 +87,7 @@ declare module '@tanstack/solid-router' {
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
ShortenRoute: ShortenRoute,
|
||||
TodoRoute: TodoRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { createRouter as createTanStackRouter } from "@tanstack/solid-router";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
|
||||
// import { getContext } from './integrations/tanstack-query/provider'
|
||||
import NotFound from "./components/NotFound";
|
||||
import { QueryClient } from "@tanstack/solid-query";
|
||||
import { ErrorComponent } from "./routes/__root";
|
||||
import { getContext } from "./integrations/tanstack-query/provider";
|
||||
import { setupRouterSsrQueryIntegration } from "@tanstack/solid-router-ssr-query";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export function getRouter() {
|
||||
const context = getContext();
|
||||
const router = createTanStackRouter({
|
||||
routeTree,
|
||||
|
||||
context: getContext(),
|
||||
context,
|
||||
|
||||
scrollRestoration: true,
|
||||
defaultPreload: "intent",
|
||||
@@ -26,7 +23,7 @@ export function getRouter() {
|
||||
|
||||
setupRouterSsrQueryIntegration({
|
||||
router,
|
||||
queryClient,
|
||||
queryClient: context.queryClient,
|
||||
});
|
||||
|
||||
return router;
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from "@tanstack/solid-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/solid-router-devtools";
|
||||
import { QueryClientProvider, QueryClient } from "@tanstack/solid-query";
|
||||
import type { Transport } from "@connectrpc/connect";
|
||||
import { TransportProvider } from "~/integrations/connect-query/solid";
|
||||
|
||||
import "@fontsource/inter/400.css";
|
||||
|
||||
@@ -18,6 +20,7 @@ import { ThemeProvider } from "~/components/theme-provider";
|
||||
|
||||
export interface MyRouterContext {
|
||||
queryClient: QueryClient;
|
||||
transport: Transport;
|
||||
}
|
||||
|
||||
export function ErrorComponent({ error }: { error: Error }) {
|
||||
@@ -35,6 +38,7 @@ export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||
|
||||
function RootComponent() {
|
||||
const context = Route.useRouteContext();
|
||||
console.log("Root context:", context());
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
@@ -42,16 +46,18 @@ function RootComponent() {
|
||||
<HeadContent />
|
||||
</head>
|
||||
<body>
|
||||
<QueryClientProvider client={context().queryClient}>
|
||||
<Suspense>
|
||||
<ThemeProvider>
|
||||
<Header />
|
||||
<Outlet />
|
||||
</ThemeProvider>
|
||||
<TransportProvider transport={context().transport}>
|
||||
<QueryClientProvider client={context().queryClient}>
|
||||
<Suspense>
|
||||
<ThemeProvider>
|
||||
<Header />
|
||||
<Outlet />
|
||||
</ThemeProvider>
|
||||
|
||||
<TanStackRouterDevtools />
|
||||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
<TanStackRouterDevtools />
|
||||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
</TransportProvider>
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
17
src/routes/shorten.tsx
Normal file
17
src/routes/shorten.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createFileRoute } from "@tanstack/solid-router";
|
||||
import ListRedirections from "~/components/shortener/listRedirections";
|
||||
import CreateRedirectionForm from "~/components/shortener/createRedirectionForm";
|
||||
|
||||
export const Route = createFileRoute("/shorten")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div class="p-4 flex flex-col gap-y-4">
|
||||
<h1 class="text-xl text-bold">URL Redirections</h1>
|
||||
<CreateRedirectionForm />
|
||||
<ListRedirections />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -5,6 +5,14 @@ globalThis.Response = FastResponse;
|
||||
|
||||
export default createServerEntry({
|
||||
fetch(request) {
|
||||
return handler.fetch(request)
|
||||
console.log("==> Server entry fetch start:", request.method, request.url);
|
||||
try {
|
||||
const res = handler.fetch(request);
|
||||
console.log("<== Server entry fetch sync return:", res);
|
||||
return res;
|
||||
} catch (e) {
|
||||
console.error("<== Server entry fetch sync throw:", e);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user