diff --git a/.gitignore b/.gitignore index 6389603..26bd9ba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ db/ .env -*sql.gz \ No newline at end of file +*sql.gz +crates/prisma/generated/client +crates.sublime-workspace diff --git a/client/src/assets/favicon.ico b/client/src/assets/favicon.ico deleted file mode 100644 index 0741914..0000000 Binary files a/client/src/assets/favicon.ico and /dev/null differ diff --git a/client/src/assets/icons/android-chrome-192x192.png b/client/src/assets/icons/android-chrome-192x192.png deleted file mode 100644 index 93ebe2e..0000000 Binary files a/client/src/assets/icons/android-chrome-192x192.png and /dev/null differ diff --git a/client/src/assets/icons/android-chrome-512x512.png b/client/src/assets/icons/android-chrome-512x512.png deleted file mode 100644 index 52d1623..0000000 Binary files a/client/src/assets/icons/android-chrome-512x512.png and /dev/null differ diff --git a/client/src/assets/icons/apple-touch-icon.png b/client/src/assets/icons/apple-touch-icon.png deleted file mode 100644 index 254e4bb..0000000 Binary files a/client/src/assets/icons/apple-touch-icon.png and /dev/null differ diff --git a/client/src/assets/icons/favicon-16x16.png b/client/src/assets/icons/favicon-16x16.png deleted file mode 100644 index e81177d..0000000 Binary files a/client/src/assets/icons/favicon-16x16.png and /dev/null differ diff --git a/client/src/assets/icons/favicon-32x32.png b/client/src/assets/icons/favicon-32x32.png deleted file mode 100644 index 40e9b5b..0000000 Binary files a/client/src/assets/icons/favicon-32x32.png and /dev/null differ diff --git a/client/src/assets/icons/mstile-150x150.png b/client/src/assets/icons/mstile-150x150.png deleted file mode 100644 index 9cfb889..0000000 Binary files a/client/src/assets/icons/mstile-150x150.png and /dev/null differ diff --git a/client/src/assets/swirl.fix.svg b/client/src/assets/swirl.fix.svg deleted file mode 100644 index 29418ca..0000000 --- a/client/src/assets/swirl.fix.svg +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/src/assets/swirl.svg b/client/src/assets/swirl.svg deleted file mode 100644 index cdae2a8..0000000 --- a/client/src/assets/swirl.svg +++ /dev/null @@ -1,541 +0,0 @@ - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates.sublime-project b/crates.sublime-project new file mode 100644 index 0000000..20c8f04 --- /dev/null +++ b/crates.sublime-project @@ -0,0 +1,31 @@ +{ + "settings": { + "LSP": { + "LSP-typescript": { + "enabled": false, + }, + "Deno": { + "enabled": true, + "settings": { + "deno.config": "./crates/deno.jsonc", + "deno.unstable": true, + "deno.importMap": "./import_map.json", + "deno.suggest.imports.hosts": { + "https://deno.land": true, + "https://some-other-cdn.com": true, + }, + }, + }, + "LSP-pyright": + { + "enabled": false + }, + }, + }, + "folders": + [ + { + "path": ".", + } + ], +} diff --git a/crates/.gitignore b/crates/.gitignore new file mode 100644 index 0000000..5b4bdef --- /dev/null +++ b/crates/.gitignore @@ -0,0 +1,11 @@ +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# Fresh build directory +_fresh/ +# npm dependencies +node_modules/ diff --git a/crates/README.md b/crates/README.md new file mode 100644 index 0000000..ec0e33e --- /dev/null +++ b/crates/README.md @@ -0,0 +1,16 @@ +# Fresh project + +Your new Fresh project is ready to go. You can follow the Fresh "Getting +Started" guide here: https://fresh.deno.dev/docs/getting-started + +### Usage + +Make sure to install Deno: https://deno.land/manual/getting_started/installation + +Then start the project: + +``` +deno task start +``` + +This will watch the project directory and restart as necessary. diff --git a/crates/api/auth.middleware.ts b/crates/api/auth.middleware.ts new file mode 100644 index 0000000..699d21e --- /dev/null +++ b/crates/api/auth.middleware.ts @@ -0,0 +1,83 @@ +import { FreshContext } from "$fresh/server.ts" +import { getCookies, setCookie } from "$std/http/cookie.ts" +import { createPg } from "api/pg.ts" + +// Define public paths that don't require authentication +const PUBLIC_PATHS = [ + "/", + "/api/auth/login", + "/api/auth/register", + "/api/auth/logout", +] + +// Sessions storage shared between routes +// In production, this would be Redis or a database +export const sessions = new Map() + +export async function authMiddleware(req: Request, ctx: FreshContext): Promise { + const url = new URL(req.url) + const path = url.pathname + + // Allow public paths without authentication + if (PUBLIC_PATHS.includes(path)) { + return await ctx.next() + } + + // Check for authentication + const cookies = getCookies(req.headers) + const sessionId = cookies.session + + if (!sessionId) { + if (path.startsWith("/api/")) { + return new Response(JSON.stringify({ error: "Not logged in" }), { + status: 401, + headers: { "Content-Type": "application/json" } + }) + } + + // Redirect to login page for non-API routes + return new Response(null, { + status: 302, + headers: { Location: "/" } + }) + } + + try { + // Verify session + const session = sessions.get(sessionId) + + if (!session || new Date() > session.expiresAt) { + if (session) { + sessions.delete(sessionId) + } + throw new Error("Invalid or expired session") + } + + // Get user from database + const pg = createPg(console) + const user = await pg.userFindId(session.userId) + + if (!user) { + throw new Error("User not found") + } + + // Add user to state for use in handlers + ctx.state.user = user + return await ctx.next() + } catch (error) { + console.error("Auth error:", error) + + if (path.startsWith("/api/")) { + return new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + headers: { "Content-Type": "application/json" } + }) + } + + // Redirect to login page for non-API routes + return new Response(null, { + status: 302, + headers: { Location: "/" } + }) + } +} \ No newline at end of file diff --git a/crates/api/pg.ts b/crates/api/pg.ts new file mode 100644 index 0000000..0306497 --- /dev/null +++ b/crates/api/pg.ts @@ -0,0 +1,85 @@ +import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts" +import { load } from "$std/dotenv/mod.ts" +import { Prisma, PrismaClient } from "prisma/generated/client/deno/edge.ts"; + +// Load environment variables +await load({ export: true }) + +// Create and export the Prisma client +export const prisma = new PrismaClient() + +export interface UserCreateParams { + email: string + password: string + discogs?: string + data?: Record +} + +export function createPg(log: Console) { + async function userFindId(id: string) { + return prisma.user.findUnique({ where: { id } }) + } + + async function userFindEmail(email: string) { + return prisma.user.findUnique({ where: { email } }) + } + + async function userWants(user: any) { + const wants = await prisma.want.findMany({ + where: { userId: user.id }, + include: { _count: { select: { offers: true } } }, + }) + + // Sort by artist and title + return wants.sort((a, b) => { + const aKey = `${a.data.artists_sort} - ${a.data.title}` + const bKey = `${b.data.artists_sort} - ${b.data.title}` + return aKey.localeCompare(bKey) + }) + } + + async function userOffers(user: any) { + return prisma.offer.findMany({ + where: { + userId: user.id, + status: { not: "declined" } // Assuming Status.Declined is "declined" + }, + include: { + want: true, + }, + orderBy: [ + { updatedAt: "asc" }, + { total: "asc" }, + { listingId: "asc" }, + ], + }) + } + + async function userCreate(params: UserCreateParams) { + const hashRounds = 10 + const password = await bcrypt.hash(params.password, hashRounds) + + const data = { + email: params.email, + password, + data: params.data || {}, + } + + return prisma.user.create({ + data, + }) + } + + return { + userFindId, + userFindEmail, + userCreate, + userWants, + userOffers, + + // Add more functions as needed + } +} + +// Export type Pg for type checking +export type Pg = ReturnType \ No newline at end of file diff --git a/client/src/components/nav.tsx b/crates/components/nav.tsx similarity index 100% rename from client/src/components/nav.tsx rename to crates/components/nav.tsx diff --git a/client/src/components/offer.group.tsx b/crates/components/offer.group.tsx similarity index 100% rename from client/src/components/offer.group.tsx rename to crates/components/offer.group.tsx diff --git a/client/src/components/offer.tsx b/crates/components/offer.tsx similarity index 100% rename from client/src/components/offer.tsx rename to crates/components/offer.tsx diff --git a/client/src/components/swirl.tsx b/crates/components/swirl.tsx similarity index 100% rename from client/src/components/swirl.tsx rename to crates/components/swirl.tsx diff --git a/client/src/components/want.tsx b/crates/components/want.tsx similarity index 100% rename from client/src/components/want.tsx rename to crates/components/want.tsx diff --git a/crates/crates.sublime-project b/crates/crates.sublime-project new file mode 100644 index 0000000..fe37df8 --- /dev/null +++ b/crates/crates.sublime-project @@ -0,0 +1,30 @@ +{ + "settings": { + "LSP": { + "LSP-typescript": { + "enabled": false, + }, + "Deno": { + "enabled": true, + "settings": { + "deno.config": "./deno.jsonc", + "deno.unstable": true, + "deno.suggest.imports.hosts": { + "https://deno.land": true, + "https://some-other-cdn.com": true, + }, + }, + }, + "LSP-pyright": + { + "enabled": false, + }, + }, + }, + "folders": + [ + { + "path": ".", + } + ], +} diff --git a/crates/crates.sublime-workspace b/crates/crates.sublime-workspace new file mode 100644 index 0000000..0d240a3 --- /dev/null +++ b/crates/crates.sublime-workspace @@ -0,0 +1,2833 @@ +{ + "auto_complete": + { + "selected_items": + [ + [ + "status", + "statusText" + ], + [ + "brows", + "Browser" + ], + [ + "parse", + "parseInt" + ], + [ + "get", + "getProperty" + ], + [ + "push", + "pushUserOffers" + ], + [ + "list", + "LISTINGS_CHANNEL" + ], + [ + "wants", + "WANTS_CHANNEL" + ], + [ + "listings", + "LISTINGS_CHANNEL" + ], + [ + "Uses", + "USERS_CHANNEL" + ], + [ + "handleof", + "handleOffers" + ], + [ + "offers", + "OFFERS_CHANNEL" + ], + [ + "AcpLisin", + "AcpListingsSyncMessage" + ], + [ + "acplis", + "acpListingsSync" + ], + [ + "grid", + "grid-template-areas" + ], + [ + "grid-", + "grid-template-areas" + ], + [ + "grid-temp", + "grid-template-areas" + ], + [ + "userOf", + "userOffers" + ], + [ + "want", + "wantId" + ], + [ + "send", + "sendOfferStatusSet" + ], + [ + "Offer", + "OfferPriceBtn" + ], + [ + "sendof", + "sendAcpOffers" + ], + [ + "acp", + "acpOffers" + ], + [ + "acpoff", + "acpOfferStatusSet" + ], + [ + "AcpOff", + "AcpOfferStatusSetMessage" + ], + [ + "AcpOf", + "AcpOffersMessage" + ], + [ + "set", + "setStatus" + ], + [ + "creates", + "createStore" + ], + [ + "open", + "openOfferStatuses" + ], + [ + "conf", + "confirmedListingIds" + ], + [ + "Eve", + "Event" + ], + [ + "WS", + "WS_PORT" + ], + [ + "koa", + "koaWebsocket" + ], + [ + "Login", + "LoginForm" + ], + [ + "H", + "Header" + ], + [ + "P", + "Provider>" + ], + [ + "Lis", + "ListingsUpDated" + ], + [ + "w", + "wantsAdded" + ], + [ + "no", + "noResultsEl" + ], + [ + "scroll", + "scrollTo" + ], + [ + "grid-templa", + "grid-template-columns" + ], + [ + "player", + "player_colours" + ], + [ + "skillitem", + "skill_item_combo" + ], + [ + "into", + "into_skill" + ], + [ + "sta", + "startIdle" + ], + [ + "component", + "componentDidMount" + ], + [ + "iter", + "iter" + ], + [ + "eff", + "effects" + ], + [ + "constru", + "constructs" + ], + [ + "assert", + "assert\tassert!(…)" + ], + [ + "back", + "background-color" + ], + [ + "eas", + "ease-in-out" + ], + [ + "game", + "game-construct" + ], + [ + "turn", + "turnCb" + ], + [ + "wig", + "wiggleEvents" + ], + [ + "aban", + "abandonBtn" + ], + [ + "lea", + "leaveBtn" + ], + [ + "Game", + "GameState" + ], + [ + "amount", + "amount_to_credits" + ], + [ + "spec", + "specialisation" + ], + [ + "we", + "Welcome" + ], + [ + "logi", + "Login" + ], + [ + "anon", + "anon_account" + ], + [ + "uuid", + "Uuid" + ], + [ + "un", + "unwrap" + ], + [ + "T", + "TARGET_DURATION_MS" + ], + [ + "pro", + "progress_durations" + ], + [ + "skill_pa", + "skill_phase_start" + ], + [ + "siphonti", + "siphon_tick_dmg" + ], + [ + "SIP", + "SIPHON_GREEN_MULT" + ], + [ + "bein", + "begin_resolve" + ], + [ + "begin", + "begin_resolve" + ], + [ + "satu", + "saturating_mul" + ], + [ + "triagetick", + "triage_tick" + ], + [ + "triagePlu", + "triage_plus_plus" + ], + [ + "sleeppl", + "sleep_plus_plus" + ], + [ + "siphontick", + "siphon_tick" + ], + [ + "siphonpl", + "siphon_plus_plus" + ], + [ + "purifypl", + "purify_plus" + ], + [ + "purgeplu", + "purge_plus_plus" + ], + [ + "purgepl", + "purge_plus" + ], + [ + "inver", + "invert" + ], + [ + "hybridbl", + "hybrid_blast" + ], + [ + "hybrid", + "hybrid_blast" + ], + [ + "haste", + "haste_strike" + ], + [ + "hastePl", + "haste_plus_plus" + ], + [ + "el", + "electrocute_plus_plus" + ], + [ + "electrocuteplpl", + "electrocute_plus_plus" + ], + [ + "elec", + "electrocute" + ], + [ + "ele", + "electrify_plus" + ], + [ + "decaytick", + "decay_tick" + ], + [ + "decaypp", + "decay_plus_plus" + ], + [ + "counteratt", + "counter_attack" + ], + [ + "counterpp", + "counter_plus_plus" + ], + [ + "count", + "counter_plus" + ], + [ + "absorbplusplu", + "absorb_plus_plus" + ], + [ + "absorbplus", + "absorb_plus" + ], + [ + "amplifypl", + "amplify_plus_plus" + ], + [ + "amplify_", + "amplify_plus" + ], + [ + "AmplifyPlu", + "AmplifyPlusPlus" + ], + [ + "adde", + "added_dmg" + ], + [ + "dmg", + "dmg_event" + ], + [ + "meta", + "Meta" + ], + [ + "new", + "new_events" + ], + [ + "event", + "event_list" + ], + [ + "Eff", + "EffectMeta" + ], + [ + "Mul", + "multiplier" + ], + [ + "bluelife", + "blue_life" + ], + [ + "green", + "green_life" + ], + [ + "addi", + "additional_skills" + ], + [ + "Hybri", + "HybridBlast" + ], + [ + "is", + "is_some" + ], + [ + "resolve_", + "resolve_phase_start" + ], + [ + "immun", + "immunity" + ], + [ + "dama", + "damage_events" + ], + [ + "skill", + "skill_phase_start" + ], + [ + "Refle", + "Reflection" + ], + [ + "modi", + "modified_power" + ], + [ + "modifi", + "modification" + ], + [ + "redh", + "red_healing" + ], + [ + "mul", + "multiplier" + ], + [ + "filter", + "filter_map" + ], + [ + "CursePlus", + "CursePlusPlus" + ], + [ + "DecayTickPlus", + "DecayTickPlusPlus" + ], + [ + "Triagetick", + "TriageTickPlusPlus" + ], + [ + "Trig", + "TriageTickPlus" + ], + [ + "Green", + "GreenPower" + ], + [ + "blue", + "blue_life" + ], + [ + "gren", + "green_life" + ] + ] + }, + "buffers": + [ + { + "file": "api/pg.ts", + "redo_stack": + [ + [ + 77, + 1, + "run_macro_file", + { + "file": "res://Packages/Default/Delete Line.sublime-macro" + }, + "AQAAAFAAAAAAAAAAnQAAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAUAAAAAAAAABQAAAAAAAAAAAAAAAAAPC/" + ] + ], + "settings": + { + "buffer_size": 2252, + "encoding": "UTF-8", + "line_ending": "Unix" + }, + "undo_stack": + [ + [ + 42, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAHYAAAAAAAAAdgAAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAdgAAAAAAAAB2AAAAAAAAAAAAAAAAAPC/" + ], + [ + 48, + 1, + "insert", + { + "characters": "$" + }, + "AQAAAHYAAAAAAAAAdwAAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAdgAAAAAAAAB2AAAAAAAAAAAAAAAAgHxA" + ], + [ + 57, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAHYAAAAAAAAAdgAAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAdgAAAAAAAAB2AAAAAAAAAAAAAAAAAPC/" + ], + [ + 58, + 1, + "vi_eval", + { + "action_args": + { + "insert_command": "vi_delete" + }, + "action_command": "enter_insert_mode", + "motion_args": + { + "extend": true, + "forward": true, + "visual": false + }, + "motion_clip_to_line": false, + "motion_command": "vi_move_by_characters_in_line", + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAHYAAAAAAAAAdgAAAAAAAAABAAAAcA", + "AQAAAAAAAAABAAAAdgAAAAAAAAB2AAAAAAAAAAAAAAAAAPC/" + ], + [ + 59, + 1, + "insert", + { + "characters": "j" + }, + "AQAAAHYAAAAAAAAAdwAAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAdgAAAAAAAAB2AAAAAAAAAAAAAAAAAPC/" + ], + [ + 60, + 1, + "left_delete", + null, + "AQAAAHYAAAAAAAAAdgAAAAAAAAABAAAAag", + "AQAAAAAAAAABAAAAdwAAAAAAAAB3AAAAAAAAAAAAAAAAAPC/" + ], + [ + 61, + 1, + "insert", + { + "characters": "p" + }, + "AQAAAHYAAAAAAAAAdwAAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAdgAAAAAAAAB2AAAAAAAAAAAAAAAAAPC/" + ] + ] + }, + { + "file": "deno.jsonc", + "settings": + { + "buffer_size": 1590, + "encoding": "UTF-8", + "line_ending": "Unix" + }, + "undo_stack": + [ + [ + 7, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAHQEAAAAAAAAdAQAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAdAQAAAAAAAB0BAAAAAAAAAAAAAAAAPC/" + ], + [ + 9, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAIsEAAAAAAAAiwQAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAiwQAAAAAAACLBAAAAAAAAAAAAAAAAE5A" + ], + [ + 11, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAJgEAAAAAAAAmAQAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAmAQAAAAAAACYBAAAAAAAAAAAAAAAAPC/" + ], + [ + 14, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAH4EAAAAAAAAfgQAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAfgQAAAAAAAB+BAAAAAAAAAAAAAAAAPC/" + ], + [ + 24, + 1, + "vi_paste_right", + null, + "AQAAAIUEAAAAAAAAmwQAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAbwQAAAAAAABvBAAAAAAAAAAAAAAAAPC/" + ], + [ + 29, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAI0EAAAAAAAAjQQAAAAAAAABAAAALw", + "AQAAAAAAAAABAAAAjQQAAAAAAACNBAAAAAAAAAAAAAAAAPC/" + ], + [ + 33, + 1, + "vi_paste_right", + null, + "AQAAALUEAAAAAAAA0AQAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAmgQAAAAAAACaBAAAAAAAAAAAAAAAAPC/" + ], + [ + 35, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAMAEAAAAAAAAwAQAAAAAAAABAAAALw", + "AQAAAAAAAAABAAAAwAQAAAAAAADABAAAAAAAAAAAAAAAAPC/" + ], + [ + 39, + 1, + "sequence", + { + "commands": + [ + [ + "move_to", + { + "register": "\"", + "to": "hardeol" + } + ], + [ + "insert", + { + "characters": "," + } + ], + [ + "exit_insert_mode", + null + ] + ] + }, + "AQAAALQEAAAAAAAAtQQAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAApQQAAAAAAAClBAAAAAAAAAAAAAAAgGBA" + ], + [ + 61, + 1, + "sequence", + { + "commands": + [ + [ + "enter_visual_mode", + null + ], + [ + "vi_eval", + { + "action_args": null, + "action_command": null, + "motion_args": + { + "by": "stops", + "empty_line": true, + "extend": true, + "forward": true, + "punct_begin": true, + "word_begin": true + }, + "motion_clip_to_line": true, + "motion_command": "move", + "motion_inclusive": false, + "motion_mode": 0 + } + ], + [ + "vi_eval", + { + "action_args": null, + "action_command": null, + "motion_args": + { + "by": "stops", + "empty_line": true, + "extend": true, + "forward": true, + "punct_begin": true, + "word_begin": true + }, + "motion_clip_to_line": true, + "motion_command": "move", + "motion_inclusive": false, + "motion_mode": 0 + } + ], + [ + "vi_eval", + { + "action_args": null, + "action_command": null, + "motion_args": + { + "by": "stops", + "empty_line": true, + "extend": true, + "forward": true, + "punct_begin": true, + "word_begin": true + }, + "motion_clip_to_line": true, + "motion_command": "move", + "motion_inclusive": false, + "motion_mode": 0 + } + ], + [ + "vi_eval", + { + "action_args": null, + "action_command": null, + "motion_args": + { + "by": "stops", + "empty_line": true, + "extend": true, + "forward": false, + "punct_begin": true, + "word_begin": true + }, + "motion_clip_to_line": true, + "motion_command": "move", + "motion_inclusive": false, + "motion_mode": 0 + } + ], + [ + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + } + ] + ] + }, + "AQAAABYBAAAAAAAAFgEAAAAAAAALAAAALS11bnN0YWJsZSA", + "AQAAAAAAAAABAAAAFgEAAAAAAAAWAQAAAAAAAAAAAAAAAPC/" + ] + ] + }, + { + "file": "api/auth.middleware.ts", + "settings": + { + "buffer_size": 2440, + "encoding": "UTF-8", + "line_ending": "Unix" + }, + "undo_stack": + [ + [ + 3, + 1, + "insert", + { + "characters": "w" + }, + "AQAAAGwAAAAAAAAAbQAAAAAAAAAAAAAA", + "AQAAAAAAAAABAAAAbAAAAAAAAABsAAAAAAAAAAAAAAAAAPC/" + ], + [ + 4, + 1, + "left_delete", + null, + "AQAAAGwAAAAAAAAAbAAAAAAAAAABAAAAdw", + "AQAAAAAAAAABAAAAbQAAAAAAAABtAAAAAAAAAAAAAAAAAPC/" + ], + [ + 8, + 1, + "vi_eval", + { + "action_args": + { + }, + "action_command": "vi_right_delete", + "motion_args": + { + }, + "motion_clip_to_line": false, + "motion_command": null, + "motion_inclusive": false, + "motion_mode": 0 + }, + "AQAAAIYAAAAAAAAAhgAAAAAAAAABAAAAJA", + "AQAAAAAAAAABAAAAhgAAAAAAAACGAAAAAAAAAAAAAAAAAPC/" + ] + ] + } + ], + "build_system": "", + "build_system_choices": + [ + ], + "build_varint": "", + "command_palette": + { + "height": 385.0, + "last_filter": "", + "selected_items": + [ + [ + "rest", + "LSP: Restart Server" + ], + [ + "resta", + "LSP: Restart Server" + ], + [ + "restart", + "LSP: Restart Server" + ], + [ + "enable", + "LSP: Enable Language Server in Project" + ], + [ + "disable", + "LSP: Disable Language Server Globally" + ], + [ + "disbal", + "LSP: Disable Language Server in Project" + ], + [ + "lsp", + "LSP: Toggle Diagnostics Panel" + ], + [ + "space", + "Indentation: Convert to Spaces" + ], + [ + "format", + "LSP: Format File" + ], + [ + "dis", + "LSP: Disable Language Server in Project" + ], + [ + "key ", + "Preferences: Key Bindings" + ], + [ + "save all", + "File: Save All" + ], + [ + "install", + "Package Control: Install Package" + ], + [ + "gruvbox", + "gruvbox: Select Theme" + ], + [ + "diff", + "Git: Diff Current File" + ], + [ + "type", + "Set Syntax: TypeScript" + ], + [ + "remove", + "Package Control: Remove Package" + ], + [ + "insta", + "Package Control: Install Package" + ], + [ + "log", + "Git: Log Current File" + ], + [ + "pretty", + "Pretty JSON: Format JSON" + ], + [ + "build", + "Build: New Build System" + ], + [ + "rein", + "Indentation: Reindent Lines" + ], + [ + "ja", + "Set Syntax: JavaScript" + ], + [ + "nginx", + "Set Syntax: nginx" + ], + [ + "jsx", + "Set Syntax: JSX" + ], + [ + "javas", + "Set Syntax: JavaScript" + ], + [ + "hask", + "Set Syntax: Haskell" + ], + [ + "enable pa", + "Package Control: Enable Package" + ], + [ + "enable pack", + "Package Control: Enable Package" + ], + [ + "restar", + "LSP: Restart Servers" + ], + [ + "disab", + "LSP: Disable Language Server Globally" + ], + [ + "lsp ser", + "LSP: Enable Language Server Globally" + ], + [ + "instal", + "Package Control: Install Package" + ], + [ + "lsp serv", + "LSP: Enable Language Server Globally" + ], + [ + "settings", + "Preferences: LSP Settings" + ], + [ + "res", + "LSP: Restart Servers" + ], + [ + "disa", + "LSP: Disable Language Server Globally" + ], + [ + "lang", + "LSP: Toggle Panel: Language Servers" + ], + [ + "lsp setti", + "Preferences: LSP Settings" + ], + [ + "enab", + "LSP: Enable Language Server in Project" + ], + [ + "disbale", + "LSP: Disable Language Server Globally" + ], + [ + "next", + "LSP: Next Diagnostic" + ], + [ + "diagnost", + "LSP: Toggle Panel: Diagnostics" + ], + [ + "enabl", + "LSP: Enable Language Server in Project" + ], + [ + "lsp settings", + "Preferences: LSP Settings" + ], + [ + "blame", + "Git: Blame" + ], + [ + "check", + "Git: Checkout Tag" + ], + [ + "preview", + "Markdown Preview: Preview in Browser" + ], + [ + "list", + "Package Control: List Packages" + ], + [ + "mark", + "Markdown Preview: Preview in Browser" + ], + [ + "chara", + "HTML: Encode Special Characters" + ], + [ + "pre", + "Pretty JSON: Format (Pretty Print) JSON" + ], + [ + "json", + "Pretty JSON: Minify (compress) JSON" + ], + [ + "less", + "Set Syntax: Less" + ], + [ + "synta", + "Set Syntax: Rust" + ], + [ + "le", + "Set Syntax: Less" + ], + [ + "checkout", + "Git: Checkout Tag" + ], + [ + "di", + "Git: Diff Current File" + ], + [ + "checkou", + "Git: Checkout Tag" + ], + [ + "spaces", + "Indentation: Convert to Spaces" + ], + [ + "l", + "Git: Log Current File" + ], + [ + "rust", + "Preferences: Rust Enhanced Settings" + ], + [ + "lo", + "Git: Log Current File" + ], + [ + "reind", + "Indentation: Reindent Lines" + ], + [ + "gruvbo", + "gruvbox: Select Theme" + ], + [ + "theme", + "UI: Select Theme" + ], + [ + "packa", + "Package Control: List Packages" + ], + [ + "them", + "UI: Select Theme" + ], + [ + "git log", + "Git: Log All" + ], + [ + "package remo", + "Package Control: Remove Package" + ], + [ + "unify", + "Line Endings Unify" + ], + [ + "inst", + "Package Control: Install Package" + ], + [ + "jav", + "Set Syntax: JavaScript" + ], + [ + "eslint", + "Preferences: SublimeLinter Settings" + ], + [ + "rename", + "Rename File" + ], + [ + "babel", + "Set Syntax: JavaScript (Babel)" + ], + [ + "linter", + "SublimeLinter: Lint This View" + ], + [ + "sublimme", + "SublimeLinter: Reload SublimeLinter and its Plugins" + ], + [ + "show", + "SublimeLinter: Show All Errors" + ], + [ + "packa upda", + "Package Control: Upgrade Package" + ], + [ + "syntas rust", + "Set Syntax: Rust" + ], + [ + "cargo", + "Set Syntax: Cargo Build Results" + ], + [ + "toggle syn", + "Rust: Toggle Syntax Checking" + ], + [ + "syntax rust", + "Rust: Toggle Syntax Checking" + ], + [ + "enhanced", + "Build With: RustEnhanced" + ], + [ + "indent", + "Indentation: Convert to Spaces" + ], + [ + "pack", + "Package Control: Upgrade Package" + ], + [ + "run", + "Build With: RustEnhanced - Run" + ], + [ + "buildsy", + "Build: New Build System" + ], + [ + "logcu", + "Git: Log Current File" + ], + [ + "bla", + "Git: Blame" + ], + [ + "lint show", + "SublimeLinter: Show All Errors" + ], + [ + "bab", + "Set Syntax: JavaScript (Babel)" + ], + [ + "lint error", + "SublimeLinter: Show All Errors" + ], + [ + "lint", + "Preferences: SublimeLinter Settings" + ], + [ + "sublime", + "SublimeLinter: Goto Error" + ], + [ + "yaml", + "Set Syntax: YAML" + ], + [ + "bash", + "Set Syntax: Shell Script (Bash)" + ], + [ + "update", + "GitHub: Update Gist" + ], + [ + "gist", + "GitHub: Private Gist from Selection" + ], + [ + "private gist", + "GitHub: Private Gist from Selection" + ], + [ + "prett", + "Pretty JSON: Format (Pretty Print) JSON" + ], + [ + "babl", + "Babel Transform" + ], + [ + "lint ne", + "SublimeLinter: Next Error" + ], + [ + "lint nex", + "SublimeLinter: Next Error" + ], + [ + "next erro", + "SublimeLinter: Next Error" + ], + [ + "errors", + "SublimeLinter: Don’t Show Errors on Save" + ], + [ + "lin error", + "SublimeLinter: Next Error" + ], + [ + "show on", + "SublimeLinter: Show Errors on Save" + ], + [ + "color", + "UI: Select Color Scheme" + ], + [ + "git", + "Git: Toggle Annotations" + ], + [ + "show on save", + "SublimeLinter: Don’t Show Errors on Save" + ], + [ + "ne", + "SublimeLinter: Next Error" + ], + [ + "lint next", + "SublimeLinter: Next Error" + ], + [ + "remv", + "Package Control: Remove Package" + ], + [ + "packa insta", + "Package Control: Install Package" + ], + [ + "shell", + "Set Syntax: Shell Script (Bash)" + ], + [ + "error", + "SublimeLinter: Show All Errors" + ], + [ + "show all", + "SublimeLinter: Show All Errors" + ], + [ + "lint erro", + "SublimeLinter: Next Error" + ], + [ + "show erro", + "SublimeLinter: Show All Errors" + ], + [ + "balme", + "Git: Blame" + ], + [ + "lint errors", + "SublimeLinter: Show All Errors" + ], + [ + "show error", + "SublimeLinter: Show All Errors" + ], + [ + "erro", + "SublimeLinter: Show All Errors" + ], + [ + "babal", + "Preferences: Babel – Default" + ], + [ + "connec", + "Q: Connect" + ], + [ + "conn", + "Q: Connect" + ] + ], + "width": 495.0 + }, + "console": + { + "height": 746.0, + "history": + [ + "aaa", + "aaaa", + "asdasdasd", + "clear", + "import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())" + ] + }, + "distraction_free": + { + "menu_visible": true, + "show_minimap": false, + "show_open_files": false, + "show_tabs": false, + "side_bar_visible": false, + "status_bar_visible": false + }, + "expanded_folders": + [ + "/home/ntr/crates/crates", + "/home/ntr/crates/crates/prisma", + "/home/ntr/crates/crates/prisma/generated", + "/home/ntr/crates/crates/prisma/generated/client", + "/home/ntr/crates/crates/prisma/generated/client/deno", + "/home/ntr/crates/crates/prisma/generated/client/runtime" + ], + "file_history": + [ + "/home/ntr/crates/crates/deno.jsonc", + "/home/ntr/crates/crates/crates.sublime-project", + "/home/ntr/crates/crates/api/auth.middleware.ts", + "/home/ntr/crates/crates/import_map.json", + "/home/ntr/crates/crates/api/pg.ts", + "/home/ntr/crates/crates.sublime-project", + "/home/ntr/crates/crates/fresh.config.ts", + "/home/ntr/crates/crates/deno.json", + "/home/ntr/crates/crates/import.map.json", + "/home/ntr/crates/crates/routes/api/auth/login.ts", + "/home/ntr/crates/crates/routes/api/auth/logout.ts", + "/home/ntr/crates/crates/routes/api/auth/register.ts", + "/home/ntr/crates/crates/import.map.jsonc", + "/home/ntr/crates/api/index.ts", + "/home/ntr/crates/crates/prisma/schema.prisma", + "/home/ntr/crates/api/prisma/schema.prisma", + "/home/ntr/crates/deno.jsonc", + "/home/ntr/crates/crates/routes/index.tsx", + "/home/ntr/crates/crates/api/auth/middleware.ts", + "/home/ntr/crates/justfile", + "/home/ntr/crates/docker-compose.yaml", + "/home/ntr/crates/api/package.json", + "/home/ntr/crates/docker-compose.K8S.yaml", + "/home/ntr/crates/client/src/app.tsx", + "/home/ntr/crates/client/src/http.ts", + "/home/ntr/crates/client/src/components/nav.tsx", + "/home/ntr/crates/.env", + "/home/ntr/crates/client/src/routes/acp.tsx", + "/home/ntr/crates/api/src/rpc.ts", + "/home/ntr/crates/api/src/offers.ts", + "/home/ntr/crates/api/src/pg.ts", + "/home/ntr/crates/client/client.nginx.conf", + "/home/ntr/crates/nginx/localhost.nginx.conf", + "/home/ntr/crates/api/src/api.ts", + "/home/ntr/mcrates/api/scrape.ts", + "/home/ntr/mcrates/api/src/discogs.ts", + "/home/ntr/mcrates/docker-compose.K8S.yaml", + "/home/ntr/mcrates/client/src/routes/info.tsx", + "/home/ntr/mcrates/api/src/events.ts", + "/home/ntr/mcrates/api/src/pg.ts", + "/home/ntr/mcrates/client/src/components/nav.tsx", + "/home/ntr/mcrates/client/src/template.html", + "/home/ntr/mcrates/api/package.json", + "/home/ntr/mcrates/client/src/routes/login.tsx", + "/home/ntr/mcrates/client/src/app.tsx", + "/home/ntr/mcrates/docker-compose.yaml", + "/home/ntr/mcrates/api/index.ts", + "/home/ntr/mcrates/api/src/services.ts", + "/home/ntr/mcrates/api/src/log.ts", + "/home/ntr/mcrates/.env", + "/home/ntr/mcrates/api/api.Dockerfile", + "/home/ntr/mcrates/api/src/api.ts", + "/home/ntr/mcrates/api/.dockerignore", + "/home/ntr/mcrates/api/node_modules/puppeteer/lib/types.d.ts", + "/home/ntr/mcrates/client/src/http.ts", + "/home/ntr/mcrates/nginx/localhost.nginx.conf", + "/home/ntr/mcrates/client/src/style/styles.less", + "/home/ntr/mcrates/api/src/utils.ts", + "/home/ntr/Downloads/error.html", + "/home/ntr/mcrates/node_modules/typescript/lib/lib.es5.d.ts", + "/home/ntr/mcrates/client/src/index.ts", + "/home/ntr/mcrates/client/src/style/fonts.less", + "/home/ntr/mcrates/api/prisma/schema.prisma", + "/home/ntr/mcrates/client/src/routes/search.tsx", + "/home/ntr/mcrates/docker-compose.MCRATES.yaml", + "/home/ntr/mcrates/TODO.md", + "/home/ntr/mcrates/client/src/routes/main.tsx", + "/home/ntr/mcrates/client/src/ws.ts", + "/home/ntr/mcrates/client/src/routes/acp.tsx", + "/home/ntr/mcrates/api/src/notify.ts", + "/home/ntr/mcrates/api/src/rpc.ts", + "/home/ntr/mcrates/client/src/components/offer.group.tsx", + "/home/ntr/mcrates/api/src/offers.ts", + "/home/ntr/mcrates/client/src/style/index.less", + "/home/ntr/mcrates/api/prisma/migrations/20221226113955_notify/migration.sql", + "/home/ntr/mcrates/api/src/listen.notify.ts", + "/home/ntr/mcrates/client/src/toast.tsx", + "/home/ntr/mcrates/client/src/routes/list.tsx", + "/home/ntr/mcrates/client/src/store.tsx", + "/home/ntr/mcrates/client/src/routes/offers.tsx", + "/home/ntr/mcrates/client/src/components/want.tsx", + "/home/ntr/mcrates/client/src/components/offer.tsx", + "/home/ntr/mcrates/client/src/components/swirl.tsx", + "/home/ntr/mcrates/client/src/components/want.offers.tsx", + "/home/ntr/etc/gear.js", + "/home/ntr/mcrates/client/src/components/header.tsx", + "/home/ntr/mcrates/client/src/routes/home.tsx", + "/home/ntr/mcrates/client/src/components/bundle.tsx", + "/home/ntr/mcrates/client/src/components/acp.offer.tsx", + "/home/ntr/mcrates/client/tsconfig.json", + "/home/ntr/mcrates/client/build/bundle.f19a5.js", + "/home/ntr/mcrates/client/bundle.f19a5.js", + "/home/ntr/mcrates/client/package.json", + "/home/ntr/mcrates/global.d.ts", + "/home/ntr/mcrates/api/global.d.ts", + "/home/ntr/mcrates/client/src/actions.ts", + "/home/ntr/mcrates/client/src/store/reducers.ts", + "/home/ntr/mcrates/client/src/store/store.ts", + "/home/ntr/mcrates/api/src/routers/router.auth.ts", + "/home/ntr/mcrates/client/src/actions.tsx", + "/home/ntr/mcrates/client/src/components/app.tsx", + "/home/ntr/mcrates/client/.babelrc.js", + "/home/ntr/mcrates/client/src/routes/bundles.tsx", + "/home/ntr/etc/kaiju.gertrude.application.txt", + "/home/ntr/mcrates/docker-compose.TEST.yaml", + "/home/ntr/mcrates/client/.gitignore", + "/home/ntr/mcrates/client/node_modules/preact-cli/lib/index.js", + "/home/ntr/mcrates/api/tsconfig.json", + "/home/ntr/mcrates/etc/matescrates.au.nginx.conf", + "/home/ntr/mcrates/client/src/routes/user.tsx", + "/home/ntr/mnml/studios/minimalstudios.com.au.nginx.conf", + "/home/ntr/mnml/studios/package.json", + "/home/ntr/mnml/client/index.html", + "/tmp/mozilla_ntr0/auto.minimal.md", + "/home/ntr/mnml/client/src/components/vbox.utils.jsx", + "/home/ntr/mnml/studios/index.js", + "/home/ntr/mnml/studios/src/app.jsx", + "/home/ntr/mnml/studios/src/components/studios.jsx", + "/home/ntr/mnml/core/src/player.rs", + "/home/ntr/Dropbox/.sketch/yep.txt", + "/home/ntr/mnml/client/src/components/vbox.info.thresholds.jsx", + "/home/ntr/mnml/client/src/components/stripe.buttons.jsx", + "/home/ntr/mnml/studios/assets/styles/styles.less", + "/home/ntr/mnml/client/assets/styles/instance.less", + "/home/ntr/mnml/client/assets/styles/account.less", + "/home/ntr/mnml/core/src/game.rs", + "/home/ntr/mnml/core/src/construct.rs", + "/home/ntr/mnml/client/src/components/vbox.info.jsx" + ], + "find": + { + "height": 37.0 + }, + "find_in_files": + { + "height": 154.0, + "where_history": + [ + "" + ] + }, + "find_state": + { + "case_sensitive": false, + "find_history": + [ + "$api", + "unstabl", + "api" + ], + "highlight": true, + "in_selection": false, + "preserve_case": false, + "regex": false, + "replace_history": + [ + ], + "reverse": false, + "scrollbar_highlights": true, + "show_context": true, + "use_buffer2": false, + "use_gitignore": true, + "whole_word": false, + "wrap": true + }, + "groups": + [ + { + "sheets": + [ + { + "buffer": 0, + "file": "api/pg.ts", + "selected": true, + "semi_transient": false, + "settings": + { + "buffer_size": 2252, + "regions": + { + }, + "selection": + [ + [ + 430, + 430 + ] + ], + "settings": + { + "auto_complete_triggers": + [ + { + "characters": "<", + "selector": "text.html, text.xml" + }, + { + "rhs_empty": true, + "selector": "punctuation.accessor" + }, + { + "characters": ".\"'`/@<#", + "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", + "server": "Deno" + } + ], + "lsp_active": true, + "lsp_uri": "file:///home/ntr/crates/crates/api/pg.ts", + "show_definitions": false, + "syntax": "Packages/JavaScript/TypeScript.sublime-syntax", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 0, + "stack_multiselect": false, + "type": "text" + }, + { + "buffer": 1, + "file": "deno.jsonc", + "semi_transient": false, + "settings": + { + "buffer_size": 1590, + "regions": + { + }, + "selection": + [ + [ + 278, + 278 + ] + ], + "settings": + { + "auto_complete_triggers": + [ + { + "characters": "<", + "selector": "text.html, text.xml" + }, + { + "rhs_empty": true, + "selector": "punctuation.accessor" + }, + { + "selector": "- comment - punctuation.separator - punctuation.definition.string.end - constant.character.escape - invalid.illegal - punctuation.section.mapping - punctuation.section.sequence", + "server": "LSP-json" + } + ], + "lsp_active": true, + "lsp_uri": "file:///home/ntr/crates/crates/deno.jsonc", + "show_definitions": false, + "syntax": "Packages/JSON/JSON.sublime-syntax", + "tab_size": 2, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 2, + "stack_multiselect": false, + "type": "text" + }, + { + "buffer": 2, + "file": "api/auth.middleware.ts", + "semi_transient": false, + "settings": + { + "buffer_size": 2440, + "regions": + { + }, + "selection": + [ + [ + 250, + 250 + ] + ], + "settings": + { + "auto_complete_triggers": + [ + { + "characters": "<", + "selector": "text.html, text.xml" + }, + { + "rhs_empty": true, + "selector": "punctuation.accessor" + }, + { + "characters": ".\"'`/@<#", + "selector": "meta.tag, source - comment - string.quoted.double.block - string.quoted.single.block - string.unquoted.heredoc", + "server": "Deno" + } + ], + "lsp_active": true, + "lsp_uri": "file:///home/ntr/crates/crates/api/auth.middleware.ts", + "show_definitions": false, + "syntax": "Packages/JavaScript/TypeScript.sublime-syntax", + "tab_size": 4, + "translate_tabs_to_spaces": true + }, + "translation.x": 0.0, + "translation.y": 0.0, + "zoom_level": 1.0 + }, + "stack_index": 1, + "stack_multiselect": false, + "type": "text" + } + ] + } + ], + "incremental_find": + { + "height": 37.0 + }, + "input": + { + "height": 149.0 + }, + "layout": + { + "cells": + [ + [ + 0, + 0, + 1, + 1 + ] + ], + "cols": + [ + 0.0, + 1.0 + ], + "rows": + [ + 0.0, + 1.0 + ] + }, + "menu_visible": true, + "output.LSP Log Panel": + { + "height": 0.0 + }, + "output.SublimeLinter": + { + "height": 0.0 + }, + "output.diagnostics": + { + "height": 753.0 + }, + "output.find_results": + { + "height": 729.0 + }, + "output.mdpopups": + { + "height": 0.0 + }, + "output.mdpopups-dummy": + { + "height": 0.0 + }, + "pinned_build_system": "", + "project": "crates.sublime-project", + "replace": + { + "height": 108.0 + }, + "save_all_on_build": true, + "select_file": + { + "height": 0.0, + "last_filter": "", + "selected_items": + [ + [ + "deno.", + "deno.jsonc" + ], + [ + "middle", + "api/auth.middleware.ts" + ], + [ + "pg", + "api/pg.ts" + ], + [ + "deno", + "deno.jsonc" + ], + [ + "fresh", + "crates/fresh.config.ts" + ], + [ + "pg.", + "crates/api/pg.ts" + ], + [ + "pg.ts", + "crates/api/pg.ts" + ], + [ + "shc", + "api/prisma/schema.prisma" + ], + [ + "package", + "api/package.json" + ], + [ + "pack", + "api/package.json" + ], + [ + "http", + "client/src/http.ts" + ], + [ + "compose", + "docker-compose.yaml" + ], + [ + "rpc", + "api/src/rpc.ts" + ], + [ + "api", + "api/src/api.ts" + ], + [ + "acp", + "client/src/routes/acp.tsx" + ], + [ + ".env", + ".env" + ], + [ + "nginx", + "nginx/localhost.nginx.conf" + ], + [ + "", + ".env" + ], + [ + "app", + "client/src/app.tsx" + ], + [ + "k", + "docker-compose.K8S.yaml" + ], + [ + "eve", + "api/src/events.ts" + ], + [ + "disco", + "api/src/discogs.ts" + ], + [ + "servi", + "api/src/services.ts" + ], + [ + "ind", + "api/index.ts" + ], + [ + "log", + "api/src/log.ts" + ], + [ + "dis", + "api/src/discogs.ts" + ], + [ + "ev", + "api/src/events.ts" + ], + [ + "scr", + "api/scrape.ts" + ], + [ + "scra", + "api/scrape.ts" + ], + [ + "packa", + "api/package.json" + ], + [ + "seri", + "api/src/services.ts" + ], + [ + "even", + "api/src/events.ts" + ], + [ + "inde", + "api/index.ts" + ], + [ + "login", + "client/src/routes/login.tsx" + ], + [ + "index", + "api/index.ts" + ], + [ + "ser", + "api/src/services.ts" + ], + [ + "utils", + "api/src/utils.ts" + ], + [ + "env", + ".env" + ], + [ + "events", + "api/src/events.ts" + ], + [ + "sty", + "client/src/style/fonts.less" + ], + [ + "style", + "client/src/style/styles.less" + ], + [ + "app.", + "client/src/app.tsx" + ], + [ + "index.", + "client/src/index.ts" + ], + [ + "dock", + "docker-compose.yaml" + ], + [ + "sche", + "api/prisma/schema.prisma" + ], + [ + "serv", + "api/src/services.ts" + ], + [ + "todo", + "TODO.md" + ], + [ + "mcr", + "docker-compose.MCRATES.yaml" + ], + [ + ".e", + ".env" + ], + [ + "st", + "client/src/style/styles.less" + ], + [ + "info", + "client/src/routes/info.tsx" + ], + [ + "docker", + "docker-compose.yaml" + ], + [ + "search", + "client/src/routes/search.tsx" + ], + [ + "disc", + "api/src/discogs.ts" + ], + [ + "main", + "client/src/routes/main.tsx" + ], + [ + "notif", + "api/src/notify.ts" + ], + [ + "ws", + "client/src/ws.ts" + ], + [ + "offer", + "client/src/components/offer.group.tsx" + ], + [ + "off", + "api/src/offers.ts" + ], + [ + "e", + "api/src/events.ts" + ], + [ + "tod", + "TODO.md" + ], + [ + "htt", + "client/src/http.ts" + ], + [ + "noti", + "api/src/notify.ts" + ], + [ + "offergr", + "client/src/components/offer.group.tsx" + ], + [ + "sear", + "client/src/routes/search.tsx" + ], + [ + "styl", + "client/src/style/styles.less" + ], + [ + "templ", + "client/src/template.html" + ], + [ + "indexle", + "client/src/style/index.less" + ], + [ + "m", + "client/src/routes/main.tsx" + ], + [ + "nav", + "client/src/components/nav.tsx" + ], + [ + "notify", + "api/prisma/migrations/20221226113955_notify/migration.sql" + ], + [ + "listen", + "api/src/listen.notify.ts" + ], + [ + "toas", + "client/src/toast.tsx" + ], + [ + "sch", + "api/prisma/schema.prisma" + ], + [ + "event", + "api/src/events.ts" + ], + [ + "prism", + "api/prisma/schema.prisma" + ], + [ + "store", + "client/src/store.tsx" + ], + [ + "want", + "client/src/components/want.tsx" + ], + [ + "list", + "client/src/routes/list.tsx" + ], + [ + "offers", + "client/src/routes/offers.tsx" + ], + [ + "offers.", + "api/src/offers.ts" + ], + [ + "searh", + "client/src/routes/search.tsx" + ], + [ + "searc", + "client/src/routes/search.tsx" + ], + [ + "index.le", + "client/src/style/index.less" + ], + [ + "ap", + "client/src/app.tsx" + ], + [ + "swi", + "client/src/components/swirl.tsx" + ], + [ + "grou", + "client/src/components/offer.group.tsx" + ], + [ + "offergrou", + "client/src/components/offer.group.tsx" + ], + [ + "indes.", + "api/index.ts" + ], + [ + "index.t", + "api/index.ts" + ], + [ + "schema", + "api/prisma/schema.prisma" + ], + [ + "of", + "client/src/routes/offers.tsx" + ], + [ + "schem", + "api/prisma/schema.prisma" + ], + [ + "hea", + "client/src/components/header.tsx" + ], + [ + "offe", + "client/src/components/offer.tsx" + ], + [ + "home", + "client/src/routes/home.tsx" + ], + [ + "head", + "client/src/components/header.tsx" + ], + [ + "ut", + "api/src/utils.ts" + ], + [ + "w", + "client/src/ws.ts" + ], + [ + "he", + "client/src/components/header.tsx" + ], + [ + "util", + "api/src/utils.ts" + ], + [ + "to", + "TODO.md" + ], + [ + "rp", + "api/src/rpc.ts" + ], + [ + "les", + "client/src/style/index.less" + ], + [ + "bund", + "client/src/components/bundle.tsx" + ], + [ + "tscon", + "client/tsconfig.json" + ], + [ + "comp", + "docker-compose.yaml" + ], + [ + "gloa", + "global.d.ts" + ], + [ + "glob", + "global.d.ts" + ], + [ + "glo", + "api/global.d.ts" + ], + [ + "bundl", + "client/src/components/bundle.tsx" + ], + [ + "act", + "client/src/actions.ts" + ], + [ + "ac", + "client/src/actions.ts" + ], + [ + "action", + "client/src/actions.ts" + ], + [ + "ngin", + "nginx/localhost.nginx.conf" + ], + [ + "compo", + "docker-compose.yaml" + ], + [ + "nginx.", + "nginx/localhost.nginx.conf" + ], + [ + "auth", + "api/src/routers/router.auth.ts" + ], + [ + "logi", + "client/src/routes/login.tsx" + ], + [ + "babe", + "client/.babelrc.js" + ], + [ + "docker-", + "docker-compose.yaml" + ], + [ + "ts", + "client/tsconfig.json" + ], + [ + "home.", + "client/src/routes/home.tsx" + ], + [ + "gitgo", + "client/.gitignore" + ], + [ + "bu", + "client/src/components/bundle.tsx" + ], + [ + "tsconf", + "client/tsconfig.json" + ], + [ + "bun", + "client/src/components/bundle.tsx" + ], + [ + "actoins", + "client/src/actions.ts" + ] + ], + "width": 0.0 + }, + "select_project": + { + "height": 494.0, + "last_filter": "", + "selected_items": + [ + [ + "", + "~/crates/crates.sublime-project" + ], + [ + "str", + "~/code/slprojects/strix.sublime-project" + ], + [ + "st", + "~/code/slprojects/strix.sublime-project" + ], + [ + "spa", + "~/code/slprojects/SpaceRace.sublime-project" + ], + [ + "mem", + "~/memestream/memestream.sublime-project" + ], + [ + "meme", + "~/memestream/memestream.sublime-project" + ], + [ + "mnm", + "~/code/slprojects/mnml.sublime-project" + ], + [ + "mn", + "~/code/slprojects/mnml.sublime-project" + ], + [ + "spe", + "~/code/slprojects/spectre.sublime-project" + ], + [ + "hu", + "~/code/slprojects/humt.sublime-project" + ], + [ + "h", + "~/code/slprojects/humt.sublime-project" + ], + [ + "hum", + "~/code/slprojects/humt.sublime-project" + ], + [ + "jeo", + "~/code/slprojects/jeopardy.sublime-project" + ], + [ + "sa", + "~/code/slprojects/dslams.sublime-workspace" + ], + [ + "api", + "~/code/slprojects/easygo.sublime-project" + ], + [ + "rct", + "~/code/slprojects/rct.sublime-project" + ], + [ + "rc", + "~/code/slprojects/rct.sublime-project" + ], + [ + "col", + "~/code/slprojects/columbus.sublime-project" + ], + [ + "r ", + "~/code/slprojects/rct.sublime-project" + ], + [ + "eas", + "~/code/slprojects/easygo.sublime-project" + ], + [ + "easy", + "~/code/slprojects/easygo.sublime-project" + ], + [ + "eg", + "~/code/slprojects/edge.sublime-workspace" + ], + [ + "web", + "~/code/slprojects/web.sublime-project" + ], + [ + "ed", + "~/code/slprojects/edge.sublime-workspace" + ], + [ + "edg", + "~/code/slprojects/edge.sublime-workspace" + ], + [ + "edge", + "~/code/slprojects/edge.sublime-workspace" + ], + [ + "je", + "~/code/slprojects/jeopardy.sublime-project" + ], + [ + "jeop", + "~/code/slprojects/jeopardy.sublime-project" + ], + [ + "dls", + "~/code/slprojects/dslams.sublime-workspace" + ], + [ + "dl", + "~/code/slprojects/dslams.sublime-workspace" + ], + [ + "j", + "~/code/slprojects/jeopardy.sublime-project" + ], + [ + "jep", + "~/code/slprojects/jeopardy.sublime-project" + ], + [ + "app", + "~/code/slprojects/savvi-app.sublime-project" + ], + [ + "ans", + "~/code/slprojects/answers.sublime-project" + ], + [ + "a", + "~/code/slprojects/answers.sublime-project" + ], + [ + "an", + "~/code/slprojects/answers.sublime-project" + ], + [ + "lun", + "~/code/slprojects/luna.sublime-project" + ], + [ + "luna", + "~/code/slprojects/luna.sublime-project" + ], + [ + "lui", + "~/code/slprojects/luna.sublime-project" + ], + [ + "abn", + "~/code/slprojects/answers.sublime-project" + ], + [ + "savvi", + "~/code/slprojects/savvi.sublime-project" + ], + [ + "answ", + "~/code/slprojects/answers.sublime-project" + ], + [ + "savvi-app", + "~/code/slprojects/savvi-app.sublime-project" + ], + [ + "savviapp", + "~/code/slprojects/savvi-app.sublime-project" + ], + [ + "savvi-a", + "~/code/slprojects/savvi-app.sublime-project" + ], + [ + "r", + "~/code/slprojects/rct.sublime-project" + ], + [ + "ew", + "~/slprojects/webclient.sublime-project" + ], + [ + "s", + "~/slprojects/squares.sublime-project" + ], + [ + "sein", + "~/slprojects/seinfeld.sublime-project" + ], + [ + "webcl", + "~/slprojects/webclient.sublime-project" + ], + [ + "fan", + "~/slprojects/fantasy.sublime-project" + ], + [ + "smoke", + "~/slprojects/smokestack.sublime-project" + ], + [ + "smok", + "~/slprojects/smokestack.sublime-project" + ], + [ + "da", + "~/slprojects/datta.sublime-project" + ], + [ + "dat", + "~/slprojects/datta.sublime-project" + ], + [ + "sq", + "~/slprojects/squares.sublime-project" + ], + [ + "data", + "~/slprojects/datta.sublime-project" + ], + [ + "sin", + "~/slprojects/seinfeld.sublime-project" + ], + [ + "se", + "~/slprojects/seinfeld.sublime-project" + ], + [ + "sei", + "~/slprojects/seinfeld.sublime-project" + ], + [ + "ein", + "~/slprojects/seinfeld.sublime-project" + ], + [ + "wel", + "~/slprojects/webclient.sublime-project" + ], + [ + "com", + "~/projects/communicator.sublime-project" + ], + [ + "co", + "~/projects/communicator.sublime-project" + ], + [ + "comm", + "~/projects/communicator.sublime-project" + ], + [ + "cio", + "~/projects/communicator.sublime-project" + ], + [ + "ampi", + "/Users/nathanr/projects/ampify.sublime-project" + ], + [ + "amp", + "/Users/nathanr/projects/ampify.sublime-project" + ], + [ + "ampif", + "/Users/nathanr/projects/ampify.sublime-project" + ], + [ + "wol", + "/Users/nathanr/projects/wolverine.sublime-project" + ], + [ + "cry", + "/Users/nathanr/projects/crypto.sublime-project" + ], + [ + "cr", + "/Users/nathanr/projects/crypto.sublime-project" + ], + [ + "cryp", + "/Users/nathanr/projects/crypto.sublime-project" + ], + [ + "wo", + "/Users/nathanr/projects/wolverine.sublime-project" + ], + [ + "rew", + "/Users/nathanr/projects/rewrite.sublime-project" + ], + [ + "rewr", + "/Users/nathanr/projects/rewrite.sublime-project" + ], + [ + "mix", + "/Users/nathanr/projects/mixmaster.sublime-project" + ], + [ + "crp", + "/Users/nathanr/projects/crypto.sublime-project" + ], + [ + "hyd", + "/Users/nathanr/projects/hydrogen.sublime-project" + ], + [ + "pok", + "/Users/nathanr/projects/poker.sublime-project" + ], + [ + "mixm", + "/Users/nathanr/projects/mixmaster.sublime-project" + ], + [ + "npm", + "/Users/nathanr/projects/mixmaster.sublime-project" + ], + [ + "wolver", + "/Users/nathanr/projects/wolverine.sublime-project" + ], + [ + "wolv", + "/Users/nathanr/projects/wolverine.sublime-project" + ], + [ + "solv", + "/Users/nathanr/projects/wolverine.sublime-project" + ], + [ + "wolve", + "/Users/nathanr/projects/wolverine.sublime-project" + ], + [ + "solver", + "/Users/nathanr/projects/wolverine.sublime-project" + ] + ], + "width": 374.0 + }, + "select_symbol": + { + "height": 385.0, + "last_filter": "", + "selected_items": + [ + ], + "width": 1131.0 + }, + "selected_group": 0, + "settings": + { + "lsp_show_inlay_hints": false + }, + "show_minimap": false, + "show_open_files": false, + "show_tabs": true, + "side_bar_visible": true, + "side_bar_width": 329.0, + "status_bar_visible": true, + "template_settings": + { + } +} diff --git a/crates/deno.jsonc b/crates/deno.jsonc new file mode 100644 index 0000000..0a9e5fb --- /dev/null +++ b/crates/deno.jsonc @@ -0,0 +1,54 @@ +{ + "lock": false, + "tasks": { + "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", + "cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -", + "manifest": "deno task cli manifest $(pwd)", + "start": "deno run -A --watch=static/,routes/ dev.ts", + "build": "deno run -A dev.ts build", + "preview": "deno run -A main.ts", + "prisma-generate": "deno run -A --unstable-detect-cjs npm:prisma@latest generate --no-engine", + "update": "deno run -A -r https://fresh.deno.dev/update ." + }, + + "imports": { + "$std/": "https://deno.land/std@0.217.0/", + "$fresh/": "https://deno.land/x/fresh@1.7.3/", + "preact": "https://esm.sh/preact@10.22.0", + "preact/": "https://esm.sh/preact@10.22.0/", + "@preact/signals": "https://esm.sh/*@preact/signals@1.2.2", + "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1", + "@twind/core": "https://esm.sh/@twind/core@1.1.3", + "@twind/preset-tailwind": "https://esm.sh/@twind/preset-tailwind@1.1.4/", + "@twind/preset-autoprefix": "https://esm.sh/@twind/preset-autoprefix@1.0.7/", + + "api/": "./api/", + "api": "./api/", + "prisma/": "./prisma/", + "prisma": "./prisma/" + }, + + "fmt": { + "useTabs": false, + "indentWidth": 4, + "semiColons": false, + "singleQuote": true, + "proseWrap": "preserve" + }, + + "lint": { + "rules": { + "tags": [ + "fresh", + "recommended" + ] + } + }, + "exclude": [ + "**/_fresh/*" + ], + "compilerOptions": { + "jsx": "react-jsx", + "jsxImportSource": "preact" + } +} diff --git a/crates/dev.ts b/crates/dev.ts new file mode 100755 index 0000000..ae73946 --- /dev/null +++ b/crates/dev.ts @@ -0,0 +1,8 @@ +#!/usr/bin/env -S deno run -A --watch=static/,routes/ + +import dev from "$fresh/dev.ts"; +import config from "./fresh.config.ts"; + +import "$std/dotenv/load.ts"; + +await dev(import.meta.url, "./main.ts", config); diff --git a/crates/fresh.config.ts b/crates/fresh.config.ts new file mode 100644 index 0000000..f3eacf9 --- /dev/null +++ b/crates/fresh.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "$fresh/server.ts"; +import twind from "$fresh/plugins/twindv1.ts"; +import twindConfig from "./twind.config.ts"; +import { authMiddleware } from "api/auth.middleware.ts"; + +export default defineConfig({ + plugins: [twind(twindConfig)], + middleware: { + auth: authMiddleware + } +}); diff --git a/crates/fresh.gen.ts b/crates/fresh.gen.ts new file mode 100644 index 0000000..bb6e6ec --- /dev/null +++ b/crates/fresh.gen.ts @@ -0,0 +1,29 @@ +// DO NOT EDIT. This file is generated by Fresh. +// This file SHOULD be checked into source version control. +// This file is automatically updated during development when running `dev.ts`. + +import * as $_404 from './routes/_404.tsx' +import * as $_app from './routes/_app.tsx' +import * as $api_auth_login from './routes/api/auth/login.ts' +import * as $api_auth_logout from './routes/api/auth/logout.ts' +import * as $api_auth_register from './routes/api/auth/register.ts' +import * as $api_auth_status from './routes/api/auth/status.ts' +import * as $index from './routes/index.tsx' + +import type { Manifest } from '$fresh/server.ts' + +const manifest = { + routes: { + './routes/_404.tsx': $_404, + './routes/_app.tsx': $_app, + './routes/api/auth/login.ts': $api_auth_login, + './routes/api/auth/logout.ts': $api_auth_logout, + './routes/api/auth/register.ts': $api_auth_register, + './routes/api/auth/status.ts': $api_auth_status, + './routes/index.tsx': $index, + }, + islands: {}, + baseUrl: import.meta.url, +} satisfies Manifest + +export default manifest diff --git a/crates/import_map.json b/crates/import_map.json new file mode 100644 index 0000000..e69de29 diff --git a/crates/main.ts b/crates/main.ts new file mode 100644 index 0000000..675f529 --- /dev/null +++ b/crates/main.ts @@ -0,0 +1,13 @@ +/// +/// +/// +/// +/// + +import "$std/dotenv/load.ts"; + +import { start } from "$fresh/server.ts"; +import manifest from "./fresh.gen.ts"; +import config from "./fresh.config.ts"; + +await start(manifest, config); diff --git a/crates/package-lock.json b/crates/package-lock.json new file mode 100644 index 0000000..c1800c6 --- /dev/null +++ b/crates/package-lock.json @@ -0,0 +1,608 @@ +{ + "name": "crates", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@prisma/client": "^6.6.0" + }, + "devDependencies": { + "prisma": "^6.6.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@prisma/client": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.6.0.tgz", + "integrity": "sha512-vfp73YT/BHsWWOAuthKQ/1lBgESSqYqAWZEYyTdGXyFAHpmewwWL2Iz6ErIzkj4aHbuc6/cGSsE6ZY+pBO04Cg==", + "hasInstallScript": true, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.6.0.tgz", + "integrity": "sha512-d8FlXRHsx72RbN8nA2QCRORNv5AcUnPXgtPvwhXmYkQSMF/j9cKaJg+9VcUzBRXGy9QBckNzEQDEJZdEOZ+ubA==", + "devOptional": true, + "dependencies": { + "esbuild": ">=0.12 <1", + "esbuild-register": "3.6.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.6.0.tgz", + "integrity": "sha512-DL6n4IKlW5k2LEXzpN60SQ1kP/F6fqaCgU/McgaYsxSf43GZ8lwtmXLke9efS+L1uGmrhtBUP4npV/QKF8s2ZQ==", + "devOptional": true + }, + "node_modules/@prisma/engines": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.6.0.tgz", + "integrity": "sha512-nC0IV4NHh7500cozD1fBoTwTD1ydJERndreIjpZr/S3mno3P6tm8qnXmIND5SwUkibNeSJMpgl4gAnlqJ/gVlg==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "6.6.0", + "@prisma/engines-version": "6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a", + "@prisma/fetch-engine": "6.6.0", + "@prisma/get-platform": "6.6.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a.tgz", + "integrity": "sha512-JzRaQ5Em1fuEcbR3nUsMNYaIYrOT1iMheenjCvzZblJcjv/3JIuxXN7RCNT5i6lRkLodW5ojCGhR7n5yvnNKrw==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.6.0.tgz", + "integrity": "sha512-Ohfo8gKp05LFLZaBlPUApM0M7k43a0jmo86YY35u1/4t+vuQH9mRGU7jGwVzGFY3v+9edeb/cowb1oG4buM1yw==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "6.6.0", + "@prisma/engines-version": "6.6.0-53.f676762280b54cd07c770017ed3711ddde35f37a", + "@prisma/get-platform": "6.6.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.6.0.tgz", + "integrity": "sha512-3qCwmnT4Jh5WCGUrkWcc6VZaw0JY7eWN175/pcb5Z6FiLZZ3ygY93UX0WuV41bG51a6JN/oBH0uywJ90Y+V5eA==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "6.6.0" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "devOptional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "devOptional": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "devOptional": true, + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true + }, + "node_modules/prisma": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.6.0.tgz", + "integrity": "sha512-SYCUykz+1cnl6Ugd8VUvtTQq5+j1Q7C0CtzKPjQ8JyA2ALh0EEJkMCS+KgdnvKW1lrxjtjCyJSHOOT236mENYg==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/config": "6.6.0", + "@prisma/engines": "6.6.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + } + } +} diff --git a/crates/package.json b/crates/package.json new file mode 100644 index 0000000..b723340 --- /dev/null +++ b/crates/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "prisma": "^6.6.0" + }, + "dependencies": { + "@prisma/client": "^6.6.0" + } +} diff --git a/api/prisma/migrations/20221226110634_initial_migration/migration.sql b/crates/prisma/migrations/20221226110634_initial_migration/migration.sql similarity index 100% rename from api/prisma/migrations/20221226110634_initial_migration/migration.sql rename to crates/prisma/migrations/20221226110634_initial_migration/migration.sql diff --git a/api/prisma/migrations/20221226113955_notify/migration.sql b/crates/prisma/migrations/20221226113955_notify/migration.sql similarity index 100% rename from api/prisma/migrations/20221226113955_notify/migration.sql rename to crates/prisma/migrations/20221226113955_notify/migration.sql diff --git a/api/prisma/migrations/migration_lock.toml b/crates/prisma/migrations/migration_lock.toml similarity index 100% rename from api/prisma/migrations/migration_lock.toml rename to crates/prisma/migrations/migration_lock.toml diff --git a/api/prisma/schema.prisma b/crates/prisma/schema.prisma similarity index 94% rename from api/prisma/schema.prisma rename to crates/prisma/schema.prisma index 1325587..dbe16d3 100644 --- a/api/prisma/schema.prisma +++ b/crates/prisma/schema.prisma @@ -1,8 +1,7 @@ generator client { provider = "prisma-client-js" - // include for node docker image - binaryTargets = ["native", "debian-openssl-1.1.x"] - previewFeatures = ["interactiveTransactions"] + previewFeatures = ["deno"] + output = "./generated/client" } datasource db { diff --git a/crates/routes/_404.tsx b/crates/routes/_404.tsx new file mode 100644 index 0000000..c63ae2e --- /dev/null +++ b/crates/routes/_404.tsx @@ -0,0 +1,27 @@ +import { Head } from "$fresh/runtime.ts"; + +export default function Error404() { + return ( + <> + + 404 - Page not found + + + + + 404 - Page not found + + The page you were looking for doesn't exist. + + Go back home + + + > + ); +} diff --git a/crates/routes/_app.tsx b/crates/routes/_app.tsx new file mode 100644 index 0000000..8b3a855 --- /dev/null +++ b/crates/routes/_app.tsx @@ -0,0 +1,16 @@ +import { type PageProps } from "$fresh/server.ts"; +export default function App({ Component }: PageProps) { + return ( + + + + + crates + + + + + + + ); +} diff --git a/crates/routes/api/auth/login.ts b/crates/routes/api/auth/login.ts new file mode 100644 index 0000000..641bcbd --- /dev/null +++ b/crates/routes/api/auth/login.ts @@ -0,0 +1,103 @@ +import { Handlers } from "$fresh/server.ts" +import { setCookie } from "$std/http/cookie.ts" +import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts" + +import { createPg } from "api/pg.ts" +import { sessions } from "api/auth.middleware.ts" + +export const handler: Handlers = { + async POST(req, _ctx) { + try { + const data = await req.json() + const { email, password } = data + + if (!email || !password) { + return new Response(JSON.stringify({ error: "Email and password required" }), { + status: 400, + headers: { "Content-Type": "application/json" } + }) + } + + // Create pg instance for database operations + const pg = createPg(console) + + // Find user by email + const user = await pg.userFindEmail(email) + + if (!user) { + return new Response(JSON.stringify({ error: "Invalid credentials" }), { + status: 401, + headers: { "Content-Type": "application/json" } + }) + } + + // Verify password + const match = await bcrypt.compare(password, user.password) + + if (!match) { + return new Response(JSON.stringify({ error: "Invalid credentials" }), { + status: 401, + headers: { "Content-Type": "application/json" } + }) + } + + // Create session + const sessionId = crypto.randomUUID() + const expiresAt = new Date() + expiresAt.setDate(expiresAt.getDate() + 7) // Session expires in 7 days + + sessions.set(sessionId, { + userId: user.id, + expiresAt + }) + + // Set cookie + const headers = new Headers({ "Content-Type": "application/json" }) + setCookie(headers, { + name: "session", + value: sessionId, + expires: expiresAt, + path: "/", + httpOnly: true, + secure: true, + sameSite: "Lax" + }) + + // Return user data + return new Response(JSON.stringify({ + email: user.email + }), { + status: 200, + headers + }) + } catch (error) { + console.error("Login error:", error) + return new Response(JSON.stringify({ error: "An error occurred" }), { + status: 500, + headers: { "Content-Type": "application/json" } + }) + } + }, + + // GET handler to check login status + async GET(req, ctx) { + // This would be handled by the auth middleware + // If the request reaches here, the user is authenticated + const user = ctx.state.user + + if (!user) { + return new Response(JSON.stringify({ error: "Not logged in" }), { + status: 401, + headers: { "Content-Type": "application/json" } + }) + } + + return new Response(JSON.stringify({ + id: user.id, + email: user.email + }), { + status: 200, + headers: { "Content-Type": "application/json" } + }) + } +} \ No newline at end of file diff --git a/crates/routes/api/auth/logout.ts b/crates/routes/api/auth/logout.ts new file mode 100644 index 0000000..ee71cd3 --- /dev/null +++ b/crates/routes/api/auth/logout.ts @@ -0,0 +1,32 @@ +import { Handlers } from "$fresh/server.ts" +import { getCookies, setCookie } from "$std/http/cookie.ts" +import { sessions } from "../../../api/auth.middleware.ts" + +export const handler: Handlers = { + async POST(req, _ctx) { + const cookies = getCookies(req.headers) + const sessionId = cookies.session + + if (sessionId) { + // Remove session from storage + sessions.delete(sessionId) + } + + // Clear the cookie + const headers = new Headers({ "Content-Type": "application/json" }) + setCookie(headers, { + name: "session", + value: "", + expires: new Date(0), + path: "/", + httpOnly: true, + secure: true, + sameSite: "Lax" + }) + + return new Response(JSON.stringify({ success: true }), { + status: 200, + headers + }) + } +} \ No newline at end of file diff --git a/crates/routes/api/auth/register.ts b/crates/routes/api/auth/register.ts new file mode 100644 index 0000000..45940f9 --- /dev/null +++ b/crates/routes/api/auth/register.ts @@ -0,0 +1,74 @@ +import { Handlers } from "$fresh/server.ts" +import { setCookie } from "$std/http/cookie.ts" +import { createPg } from "../../../api/pg.ts" +import { sessions } from "../../../api/auth.middleware.ts" + +export const handler: Handlers = { + async POST(req, _ctx) { + try { + const data = await req.json() + const { discogs, email, password } = data + + if (!email || !password) { + return new Response(JSON.stringify({ error: "Email and password required" }), { + status: 400, + headers: { "Content-Type": "application/json" } + }) + } + + // Create pg instance for database operations + const pg = createPg(console) + + // Check if user exists + const existingUser = await pg.userFindEmail(email) + + if (existingUser) { + return new Response(JSON.stringify({ error: "Email already registered" }), { + status: 400, + headers: { "Content-Type": "application/json" } + }) + } + + // Create user + const params = { discogs, email, password, data: {} } + const user = await pg.userCreate(params) + + // Create session + const sessionId = crypto.randomUUID() + const expiresAt = new Date() + expiresAt.setDate(expiresAt.getDate() + 7) // Session expires in 7 days + + sessions.set(sessionId, { + userId: user.id, + expiresAt + }) + + // Set cookie + const headers = new Headers({ "Content-Type": "application/json" }) + setCookie(headers, { + name: "session", + value: sessionId, + expires: expiresAt, + path: "/", + httpOnly: true, + secure: true, + sameSite: "Lax" + }) + + // Return user data + return new Response(JSON.stringify({ + id: user.id, + email: user.email + }), { + status: 200, + headers + }) + } catch (error) { + console.error("Registration error:", error) + return new Response(JSON.stringify({ error: "An error occurred" }), { + status: 500, + headers: { "Content-Type": "application/json" } + }) + } + } +} \ No newline at end of file diff --git a/crates/routes/api/auth/status.ts b/crates/routes/api/auth/status.ts new file mode 100644 index 0000000..89ab14d --- /dev/null +++ b/crates/routes/api/auth/status.ts @@ -0,0 +1,18 @@ +import { Handlers } from "$fresh/server.ts" + +export const handler: Handlers = { + async GET(_req, ctx) { + const user = ctx.state.user + + return new Response(JSON.stringify({ + authenticated: true, + user: { + id: user.id, + email: user.email + } + }), { + status: 200, + headers: { "Content-Type": "application/json" } + }) + } +} \ No newline at end of file diff --git a/crates/routes/index.tsx b/crates/routes/index.tsx new file mode 100644 index 0000000..ec74e51 --- /dev/null +++ b/crates/routes/index.tsx @@ -0,0 +1,23 @@ +import { useSignal } from "@preact/signals"; + +export default function Home() { + const count = useSignal(3); + return ( + + + + Welcome to crates + + Try updating this message in the + ./routes/index.tsx file, and recrates. + + + + ); +} diff --git a/crates/static/favicon.ico b/crates/static/favicon.ico new file mode 100644 index 0000000..1cfaaa2 Binary files /dev/null and b/crates/static/favicon.ico differ diff --git a/crates/static/logo.svg b/crates/static/logo.svg new file mode 100644 index 0000000..ef2fbe4 --- /dev/null +++ b/crates/static/logo.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/crates/twind.config.ts b/crates/twind.config.ts new file mode 100644 index 0000000..eb80572 --- /dev/null +++ b/crates/twind.config.ts @@ -0,0 +1,10 @@ +import { defineConfig, Preset } from "@twind/core"; +import presetTailwind from "@twind/preset-tailwind"; +import presetAutoprefix from "@twind/preset-autoprefix"; + +export default { + ...defineConfig({ + presets: [presetTailwind() as Preset, presetAutoprefix() as Preset], + }), + selfURL: import.meta.url, +};
+ The page you were looking for doesn't exist. +
+ Try updating this message in the + ./routes/index.tsx file, and recrates. +
./routes/index.tsx