From 3e8825d26da2aa820e9ee6b9bf2770e6eb55c671 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 12:38:48 +0000 Subject: [PATCH 1/4] Initial plan From ada9709cee30840d271cfa1bf55dbac855906895 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 12:45:35 +0000 Subject: [PATCH 2/4] Add impersonate audit logs table, logging, and admin page Co-authored-by: ArcaEge <40526225+ArcaEge@users.noreply.github.com> --- .devcontainer/devcontainer.json | 6 +- .github/dependabot.yml | 8 +- .prettierrc | 5 +- docker-compose-staging.yaml | 2 +- docker-compose.yaml | 2 +- drizzle/0022_pale_maginty.sql | 9 + drizzle/meta/0022_snapshot.json | 1165 +++++++++++++++++ drizzle/meta/_journal.json | 7 + eslint.config.js | 14 +- package-lock.json | 28 +- src/app.css | 10 +- src/lib/components/Button.svelte | 2 +- src/lib/components/OrpheusFlag.svelte | 1 - src/lib/components/Snowfall.svelte | 220 ++-- src/lib/defs.ts | 2 +- src/lib/server/airtable.ts | 4 +- src/lib/server/auth.ts | 3 +- src/lib/server/db/index.ts | 2 +- src/lib/server/db/schema.ts | 19 +- src/lib/server/slack.ts | 2 +- src/routes/+error.svelte | 12 +- src/routes/+layout.svelte | 2 +- src/routes/+page.server.ts | 2 +- src/routes/Footer.svelte | 5 +- src/routes/Rules.svelte | 19 +- src/routes/Shop.svelte | 6 +- src/routes/approved-editors/+page.svelte | 3 +- .../create-hackatime-account/+page.svelte | 16 +- src/routes/auth/failed/+page.svelte | 14 +- src/routes/auth/ineligible/+page.svelte | 16 +- src/routes/auth/logout/+server.ts | 2 +- src/routes/dashboard/+error.svelte | 6 +- src/routes/dashboard/+layout.svelte | 2 +- src/routes/dashboard/Sidebar.svelte | 14 +- src/routes/dashboard/SidebarButton.svelte | 5 +- src/routes/dashboard/admin/admin/+page.svelte | 11 +- .../admin/impersonate-logs/+page.server.ts | 55 + .../admin/admin/impersonate-logs/+page.svelte | 74 ++ .../admin/admin/market/MarketItem.svelte | 2 +- .../admin/admin/stats/+page.server.ts | 16 +- .../admin/admin/users/+page.server.ts | 2 +- .../admin/admin/users/[id]/+page.server.ts | 8 +- .../admin/ysws-review/[id]/+page.svelte | 4 +- src/routes/dashboard/explore/+page.server.ts | 14 +- src/routes/dashboard/explore/+page.svelte | 7 +- .../dashboard/market/MarketTimer.svelte | 2 +- src/routes/dashboard/projects/+page.svelte | 2 +- .../dashboard/projects/[id]/+page.server.ts | 2 +- .../dashboard/projects/[id]/+page.svelte | 4 +- .../projects/[id]/delete/+page.server.ts | 18 +- .../dashboard/projects/create/+page.svelte | 6 +- src/routes/dashboard/users/+page.server.ts | 4 +- wrangler.jsonc | 4 +- 53 files changed, 1589 insertions(+), 281 deletions(-) create mode 100644 drizzle/0022_pale_maginty.sql create mode 100644 drizzle/meta/0022_snapshot.json create mode 100644 src/routes/dashboard/admin/admin/impersonate-logs/+page.server.ts create mode 100644 src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fc2fe38..e4e748f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,9 +10,7 @@ "ghcr.io/devcontainers/features/github-cli:1": {} }, // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [ - 5173 - ], + "forwardPorts": [5173], // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "npm install", // Configure tool-specific properties. @@ -30,4 +28,4 @@ } // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" -} \ No newline at end of file +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f33a02c..3cddc75 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,7 @@ version: 2 updates: - - package-ecosystem: "devcontainers" - directory: "/" - schedule: - interval: weekly + - package-ecosystem: 'devcontainers' + directory: '/' + schedule: + interval: weekly diff --git a/.prettierrc b/.prettierrc index 8855237..8103a0b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,10 +3,7 @@ "singleQuote": true, "trailingComma": "none", "printWidth": 100, - "plugins": [ - "prettier-plugin-svelte", - "prettier-plugin-tailwindcss" - ], + "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], "overrides": [ { "files": "*.svelte", diff --git a/docker-compose-staging.yaml b/docker-compose-staging.yaml index e2f656b..5186ed1 100644 --- a/docker-compose-staging.yaml +++ b/docker-compose-staging.yaml @@ -18,4 +18,4 @@ services: image: ghcr.io/hackclub/construct:staging networks: {} volumes: - constructdb: \ No newline at end of file + constructdb: diff --git a/docker-compose.yaml b/docker-compose.yaml index 6497457..8d67fc0 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -18,4 +18,4 @@ services: image: ghcr.io/hackclub/construct:production networks: {} volumes: - constructdb: \ No newline at end of file + constructdb: diff --git a/drizzle/0022_pale_maginty.sql b/drizzle/0022_pale_maginty.sql new file mode 100644 index 0000000..11070c8 --- /dev/null +++ b/drizzle/0022_pale_maginty.sql @@ -0,0 +1,9 @@ +CREATE TABLE "impersonate_audit_log" ( + "id" serial PRIMARY KEY NOT NULL, + "adminUserId" integer NOT NULL, + "targetUserId" integer NOT NULL, + "timestamp" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "impersonate_audit_log" ADD CONSTRAINT "impersonate_audit_log_adminUserId_user_id_fk" FOREIGN KEY ("adminUserId") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "impersonate_audit_log" ADD CONSTRAINT "impersonate_audit_log_targetUserId_user_id_fk" FOREIGN KEY ("targetUserId") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/drizzle/meta/0022_snapshot.json b/drizzle/meta/0022_snapshot.json new file mode 100644 index 0000000..f53b553 --- /dev/null +++ b/drizzle/meta/0022_snapshot.json @@ -0,0 +1,1165 @@ +{ + "id": "aeaef46a-9fd1-4347-a0b5-3c6d48ca5c1a", + "prevId": "a04b623b-57d1-4225-ad78-e97531504ffc", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.devlog": { + "name": "devlog", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timeSpent": { + "name": "timeSpent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "devlog_userId_user_id_fk": { + "name": "devlog_userId_user_id_fk", + "tableFrom": "devlog", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "devlog_projectId_project_id_fk": { + "name": "devlog_projectId_project_id_fk", + "tableFrom": "devlog", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.impersonate_audit_log": { + "name": "impersonate_audit_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "adminUserId": { + "name": "adminUserId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetUserId": { + "name": "targetUserId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "impersonate_audit_log_adminUserId_user_id_fk": { + "name": "impersonate_audit_log_adminUserId_user_id_fk", + "tableFrom": "impersonate_audit_log", + "tableTo": "user", + "columnsFrom": [ + "adminUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "impersonate_audit_log_targetUserId_user_id_fk": { + "name": "impersonate_audit_log_targetUserId_user_id_fk", + "tableFrom": "impersonate_audit_log", + "tableTo": "user", + "columnsFrom": [ + "targetUserId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.legion_review": { + "name": "legion_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filamentUsed": { + "name": "filamentUsed", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "legion_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "legion_review_userId_user_id_fk": { + "name": "legion_review_userId_user_id_fk", + "tableFrom": "legion_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "legion_review_projectId_project_id_fk": { + "name": "legion_review_projectId_project_id_fk", + "tableFrom": "legion_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.market_item": { + "name": "market_item", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "createdBy": { + "name": "createdBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "minRequiredShopScore": { + "name": "minRequiredShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "minShopScore": { + "name": "minShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "maxShopScore": { + "name": "maxShopScore", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "maxPrice": { + "name": "maxPrice", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "minPrice": { + "name": "minPrice", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "isPublic": { + "name": "isPublic", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "market_item_createdBy_user_id_fk": { + "name": "market_item_createdBy_user_id_fk", + "tableFrom": "market_item", + "tableTo": "user", + "columnsFrom": [ + "createdBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.market_item_order": { + "name": "market_item_order", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "addressId": { + "name": "addressId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bricksPaid": { + "name": "bricksPaid", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "market_order_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'awaiting_approval'" + }, + "userNotes": { + "name": "userNotes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "market_item_order_userId_user_id_fk": { + "name": "market_item_order_userId_user_id_fk", + "tableFrom": "market_item_order", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'building'" + }, + "printedBy": { + "name": "printedBy", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "submittedToAirtable": { + "name": "submittedToAirtable", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "project_printedBy_user_id_fk": { + "name": "project_printedBy_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "printedBy" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ship": { + "name": "ship", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "editorFileType": { + "name": "editorFileType", + "type": "editor_file_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "editorUrl": { + "name": "editorUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploadedFileUrl": { + "name": "uploadedFileUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "modelFile": { + "name": "modelFile", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ship_userId_user_id_fk": { + "name": "ship_userId_user_id_fk", + "tableFrom": "ship", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ship_projectId_project_id_fk": { + "name": "ship_projectId_project_id_fk", + "tableFrom": "ship", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t1_review": { + "name": "t1_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "t1_review_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t1_review_userId_user_id_fk": { + "name": "t1_review_userId_user_id_fk", + "tableFrom": "t1_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t1_review_projectId_project_id_fk": { + "name": "t1_review_projectId_project_id_fk", + "tableFrom": "t1_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.t2_review": { + "name": "t2_review", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shopScoreMultiplier": { + "name": "shopScoreMultiplier", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "t2_review_userId_user_id_fk": { + "name": "t2_review_userId_user_id_fk", + "tableFrom": "t2_review", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "t2_review_projectId_project_id_fk": { + "name": "t2_review_projectId_project_id_fk", + "tableFrom": "t2_review", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "idvId": { + "name": "idvId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "idvToken": { + "name": "idvToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "profilePicture": { + "name": "profilePicture", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hackatimeTrust": { + "name": "hackatimeTrust", + "type": "hackatime_trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "trust": { + "name": "trust", + "type": "trust", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'blue'" + }, + "clay": { + "name": "clay", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "brick": { + "name": "brick", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "shopScore": { + "name": "shopScore", + "type": "real", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "hasBasePrinter": { + "name": "hasBasePrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT1Review": { + "name": "hasT1Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasT2Review": { + "name": "hasT2Review", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "hasAdmin": { + "name": "hasAdmin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isPrinter": { + "name": "isPrinter", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "lastLoginAt": { + "name": "lastLoginAt", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_idvId_unique": { + "name": "user_idvId_unique", + "nullsNotDistinct": false, + "columns": [ + "idvId" + ] + }, + "user_slackId_unique": { + "name": "user_slackId_unique", + "nullsNotDistinct": false, + "columns": [ + "slackId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.editor_file_type": { + "name": "editor_file_type", + "schema": "public", + "values": [ + "url", + "upload" + ] + }, + "public.hackatime_trust": { + "name": "hackatime_trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + }, + "public.legion_action": { + "name": "legion_action", + "schema": "public", + "values": [ + "mark_for_printing", + "unmark_for_printing", + "print", + "add_comment", + "reject", + "already_printed" + ] + }, + "public.market_order_status": { + "name": "market_order_status", + "schema": "public", + "values": [ + "awaiting_approval", + "fulfilled", + "denied", + "refunded" + ] + }, + "public.project_audit_log_type": { + "name": "project_audit_log_type", + "schema": "public", + "values": [ + "create", + "update", + "delete" + ] + }, + "public.status": { + "name": "status", + "schema": "public", + "values": [ + "building", + "submitted", + "t1_approved", + "printing", + "printed", + "t2_approved", + "finalized", + "rejected", + "rejected_locked" + ] + }, + "public.t1_review_action": { + "name": "t1_review_action", + "schema": "public", + "values": [ + "approve", + "approve_no_print", + "add_comment", + "reject", + "reject_lock" + ] + }, + "public.trust": { + "name": "trust", + "schema": "public", + "values": [ + "green", + "blue", + "yellow", + "red" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 0f8a951..d08b7cb 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -155,6 +155,13 @@ "when": 1767215125050, "tag": "0021_perfect_meteorite", "breakpoints": true + }, + { + "idx": 22, + "version": "7", + "when": 1767271332880, + "tag": "0022_pale_maginty", + "breakpoints": true } ] } \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index e78afbd..2c49fa6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -21,16 +21,14 @@ export default defineConfig( languageOptions: { globals: { ...globals.browser, ...globals.node } }, - rules: { // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. - // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - "no-undef": 'off' } + rules: { + // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + 'no-undef': 'off' + } }, { - files: [ - '**/*.svelte', - '**/*.svelte.ts', - '**/*.svelte.js' - ], + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], languageOptions: { parserOptions: { projectService: true, diff --git a/package-lock.json b/package-lock.json index f6c020c..626ab37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -984,7 +984,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3028,7 +3027,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -3050,7 +3048,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", "license": "Apache-2.0", - "peer": true, "engines": { "node": "^18.19.0 || >=20.6.0" }, @@ -3063,7 +3060,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -3079,7 +3075,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.208.0", "import-in-the-middle": "^2.0.0", @@ -3467,7 +3462,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" @@ -3484,7 +3478,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", @@ -3502,7 +3495,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -5309,7 +5301,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.47.0.tgz", "integrity": "sha512-mznN01MBXtr4T7X/E3ENkhF6GzqxTxL6/whG3OzCzUu8G8KYRNiCdoxLMVWAHJx/mDMPP3XAeKCMZHF/Xd/CDw==", "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", @@ -5348,7 +5339,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", @@ -5855,7 +5845,6 @@ "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.1", "@typescript-eslint/types": "8.46.1", @@ -6111,7 +6100,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6388,7 +6376,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -7002,7 +6989,6 @@ "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -7099,7 +7085,6 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9399,7 +9384,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -9521,7 +9505,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -9694,7 +9677,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9711,7 +9693,6 @@ "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" @@ -10017,7 +9998,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -10595,7 +10575,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.40.1.tgz", "integrity": "sha512-0R3t2oiLxJNJb2buz61MNfPdkjeyj2qTCM7TtIv/4ZfF12zD7Ig8iIo+C8febroy+9S4QJ7qfijtearSdO/1ww==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -10688,8 +10667,7 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", @@ -10854,7 +10832,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10913,7 +10890,6 @@ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.21.tgz", "integrity": "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==", "license": "MIT", - "peer": true, "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", @@ -11064,7 +11040,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11640,7 +11615,6 @@ "integrity": "sha512-HwaJmXO3M1r4S8x2ea2vy8Rw/y/38HRQuK/gNDRQ7w9cJXn6xSl1sIIqKCffULSUjul3wV3I3Nd/GfbmsRReEA==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "bin": { "workerd": "bin/workerd" }, diff --git a/src/app.css b/src/app.css index b1dd974..0dcd37b 100644 --- a/src/app.css +++ b/src/app.css @@ -254,19 +254,19 @@ } .themed-input-light { - @apply border-3 border-primary-700 bg-primary-900 fill-primary-50 p-2 text-sm ring-primary-900 placeholder:text-primary-700 active:ring-3 outline-transparent; + @apply border-3 border-primary-700 bg-primary-900 fill-primary-50 p-2 text-sm ring-primary-900 outline-transparent placeholder:text-primary-700 active:ring-3; } .themed-input { - @apply bg-primary-950 border-3 border-primary-900 ring-primary-700 focus:ring-2 transition-shadow rounded-lg placeholder:text-primary-800 outline-transparent; + @apply rounded-lg border-3 border-primary-900 bg-primary-950 ring-primary-700 outline-transparent transition-shadow placeholder:text-primary-800 focus:ring-2; } .themed-input-on-box { - @apply bg-primary-900 border-3 border-primary-800 ring-primary-700 focus:ring-2 transition-shadow rounded-lg placeholder:text-primary-700 outline-transparent; + @apply rounded-lg border-3 border-primary-800 bg-primary-900 ring-primary-700 outline-transparent transition-shadow placeholder:text-primary-700 focus:ring-2; } .checkbox { - @apply border-2 border-primary-600 bg-primary-900 ring-0 h-4 w-4 rounded-sm; + @apply h-4 w-4 rounded-sm border-2 border-primary-600 bg-primary-900 ring-0; } } @@ -276,6 +276,6 @@ } .center { - @apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2; + @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform; } } diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte index f693cc4..be1604b 100644 --- a/src/lib/components/Button.svelte +++ b/src/lib/components/Button.svelte @@ -11,7 +11,7 @@ diff --git a/src/lib/components/OrpheusFlag.svelte b/src/lib/components/OrpheusFlag.svelte index 7ddb956..9ccf5cc 100644 --- a/src/lib/components/OrpheusFlag.svelte +++ b/src/lib/components/OrpheusFlag.svelte @@ -14,4 +14,3 @@ alt="Hack Club" /> - diff --git a/src/lib/components/Snowfall.svelte b/src/lib/components/Snowfall.svelte index 3b83393..18080a5 100644 --- a/src/lib/components/Snowfall.svelte +++ b/src/lib/components/Snowfall.svelte @@ -1,127 +1,129 @@ + + - - diff --git a/src/lib/defs.ts b/src/lib/defs.ts index d3b5824..76e1bb5 100644 --- a/src/lib/defs.ts +++ b/src/lib/defs.ts @@ -15,4 +15,4 @@ export const PRINT_PAYOUT_CENTS_PER_GRAM = 2.0; export const PRINT_MINUTES_PER_GRAM = 0.24; export const BETA_DATE_CUTOFF = new Date('2025-12-16'); -export const BETA_MULTIPLIER = 1.5; \ No newline at end of file +export const BETA_MULTIPLIER = 1.5; diff --git a/src/lib/server/airtable.ts b/src/lib/server/airtable.ts index d84b547..ce5914c 100644 --- a/src/lib/server/airtable.ts +++ b/src/lib/server/airtable.ts @@ -1,4 +1,6 @@ import Airtable from 'airtable'; import { env } from '$env/dynamic/private'; -export const airtableBase = env.AIRTABLE_TOKEN ? new Airtable({ apiKey: env.AIRTABLE_TOKEN }).base(env.AIRTABLE_BASE) : null; +export const airtableBase = env.AIRTABLE_TOKEN + ? new Airtable({ apiKey: env.AIRTABLE_TOKEN }).base(env.AIRTABLE_BASE) + : null; diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 2e4b954..c1120e6 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -49,7 +49,8 @@ export async function validateSessionToken(token: string) { return { session: null, user: null }; } - const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * (SESSION_EXPIRY_DAYS / 2); + const renewSession = + Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * (SESSION_EXPIRY_DAYS / 2); if (renewSession) { session.expiresAt = new Date(Date.now() + DAY_IN_MS * SESSION_EXPIRY_DAYS); await db diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts index cc98b8d..af71f88 100644 --- a/src/lib/server/db/index.ts +++ b/src/lib/server/db/index.ts @@ -3,7 +3,7 @@ import { Pool } from 'pg'; import * as schema from './schema'; const pool = new Pool({ - connectionString: process.env.DATABASE_URL!, + connectionString: process.env.DATABASE_URL! }); export const db = drizzle(pool, { schema }); diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 5791543..5115085 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -237,14 +237,16 @@ export const marketOrderStatus = pgEnum('market_order_status', [ export const marketItemOrder = pgTable('market_item_order', { id: serial().primaryKey(), - userId: integer().references(() => user.id).notNull(), + userId: integer() + .references(() => user.id) + .notNull(), addressId: text().notNull(), bricksPaid: integer().notNull(), status: marketOrderStatus().notNull().default('awaiting_approval'), userNotes: text().notNull(), - notes: text(), // stuff like tracking code, shown to user + notes: text(), // stuff like tracking code, shown to user deleted: boolean().notNull().default(false), createdAt: timestamp().notNull().defaultNow() @@ -288,9 +290,22 @@ export const marketItemOrder = pgTable('market_item_order', { // updatedAt: timestamp().notNull().defaultNow() // }); +// Impersonate audit logs +export const impersonateAuditLog = pgTable('impersonate_audit_log', { + id: serial().primaryKey(), + adminUserId: integer() + .notNull() + .references(() => user.id), // Admin who performed the impersonation + targetUserId: integer() + .notNull() + .references(() => user.id), // User who was impersonated + timestamp: timestamp().notNull().defaultNow() +}); + export type Session = typeof session.$inferSelect; export type User = typeof user.$inferSelect; export type Project = typeof project.$inferSelect; +export type ImpersonateAuditLog = typeof impersonateAuditLog.$inferSelect; export type T1Review = typeof t1Review.$inferSelect; export type LegionReview = typeof legionReview.$inferSelect; diff --git a/src/lib/server/slack.ts b/src/lib/server/slack.ts index dfc0d4e..12962e6 100644 --- a/src/lib/server/slack.ts +++ b/src/lib/server/slack.ts @@ -13,7 +13,7 @@ export async function sendSlackDM(userId: string, message: string) { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` + Authorization: `Bearer ${token}` }, body: JSON.stringify({ channel: userId, diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index c378729..dd58336 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -2,10 +2,16 @@ import { page } from '$app/state'; -
+

oh no!!!

Error {page.status}

{page.error?.message}

- {`error - Go back home + {`error + Go back home
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index e45cb52..eeeba66 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -23,4 +23,4 @@ :global(body) { position: relative; } - \ No newline at end of file + diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index cb20996..d175818 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -3,6 +3,6 @@ import { env } from '$env/dynamic/private'; export function load({ locals }) { return { loggedIn: locals.session !== null, - idvDomain: env.IDV_DOMAIN, + idvDomain: env.IDV_DOMAIN }; } diff --git a/src/routes/Footer.svelte b/src/routes/Footer.svelte index b411335..f7bf211 100644 --- a/src/routes/Footer.svelte +++ b/src/routes/Footer.svelte @@ -8,7 +8,10 @@ A Hack Club program. Made by teens, for teens! {#if env.PUBLIC_COMMIT} - · Build {env.PUBLIC_COMMIT.slice(0, 7)} + · Build {env.PUBLIC_COMMIT.slice(0, 7)} {/if}

diff --git a/src/routes/Rules.svelte b/src/routes/Rules.svelte index a79d34e..cf3f7b3 100644 --- a/src/routes/Rules.svelte +++ b/src/routes/Rules.svelte @@ -11,10 +11,7 @@
  • You must be 13-18 years old.
  • You cannot be banned from Hackatime.
  • - You must be part of the + You must be part of the Hack Club Slack community .
  • @@ -33,15 +30,13 @@ - \ No newline at end of file + diff --git a/src/routes/Shop.svelte b/src/routes/Shop.svelte index 419c97d..d719baf 100644 --- a/src/routes/Shop.svelte +++ b/src/routes/Shop.svelte @@ -20,11 +20,7 @@ text="Cool rocket but there are better ones ∙ 15 hours" imgsrc={prize02} /> - +

    ...and more!

    diff --git a/src/routes/approved-editors/+page.svelte b/src/routes/approved-editors/+page.svelte index 2e4effd..555dd8c 100644 --- a/src/routes/approved-editors/+page.svelte +++ b/src/routes/approved-editors/+page.svelte @@ -46,7 +46,8 @@

    Want to get something else added here? Ask in #construct-help#construct-help!

    diff --git a/src/routes/auth/create-hackatime-account/+page.svelte b/src/routes/auth/create-hackatime-account/+page.svelte index 62d3c53..2e73338 100644 --- a/src/routes/auth/create-hackatime-account/+page.svelte +++ b/src/routes/auth/create-hackatime-account/+page.svelte @@ -1,13 +1,13 @@ -
    -

    Hackatime account not found

    - -

    Make sure you have a Hackatime account associated with your Slack account.

    - Make one? - Try again -
    \ No newline at end of file +
    +

    Hackatime account not found

    + +

    Make sure you have a Hackatime account associated with your Slack account.

    + Make one? + Try again +
    diff --git a/src/routes/auth/failed/+page.svelte b/src/routes/auth/failed/+page.svelte index 15ec7c7..8186463 100644 --- a/src/routes/auth/failed/+page.svelte +++ b/src/routes/auth/failed/+page.svelte @@ -1,12 +1,12 @@ -
    -

    Authentication failed

    - -

    Authentication failed.

    - Try again? -
    \ No newline at end of file +
    +

    Authentication failed

    + +

    Authentication failed.

    + Try again? +
    diff --git a/src/routes/auth/ineligible/+page.svelte b/src/routes/auth/ineligible/+page.svelte index d882dad..2ffb081 100644 --- a/src/routes/auth/ineligible/+page.svelte +++ b/src/routes/auth/ineligible/+page.svelte @@ -1,14 +1,14 @@ -
    -

    Ineligible

    - -

    Sorry, you're ineligible to take part in this event until you get verified.

    - Get verified -
    \ No newline at end of file +
    +

    Ineligible

    + +

    Sorry, you're ineligible to take part in this event until you get verified.

    + Get verified +
    diff --git a/src/routes/auth/logout/+server.ts b/src/routes/auth/logout/+server.ts index 0f94be2..bd7c790 100644 --- a/src/routes/auth/logout/+server.ts +++ b/src/routes/auth/logout/+server.ts @@ -9,5 +9,5 @@ export async function GET(event) { deleteSessionTokenCookie(event); } - return redirect(302, "/"); + return redirect(302, '/'); } diff --git a/src/routes/dashboard/+error.svelte b/src/routes/dashboard/+error.svelte index c1c00a2..b890f6b 100644 --- a/src/routes/dashboard/+error.svelte +++ b/src/routes/dashboard/+error.svelte @@ -8,6 +8,10 @@

    oh no!!!

    Error {page.status}

    {page.error?.message}

    - {`error + {`error Go back home diff --git a/src/routes/dashboard/+layout.svelte b/src/routes/dashboard/+layout.svelte index fcbefe3..5448dd9 100644 --- a/src/routes/dashboard/+layout.svelte +++ b/src/routes/dashboard/+layout.svelte @@ -1,6 +1,6 @@ diff --git a/src/routes/dashboard/Sidebar.svelte b/src/routes/dashboard/Sidebar.svelte index be067c1..750c02a 100644 --- a/src/routes/dashboard/Sidebar.svelte +++ b/src/routes/dashboard/Sidebar.svelte @@ -8,12 +8,8 @@ ClipboardPen, ClipboardPenLine, Store, - ShieldUser, - Box - - } from '@lucide/svelte'; import { page } from '$app/state'; import logo from '$lib/assets/logo.png'; @@ -37,9 +33,7 @@ Explore Market {#if user.isPrinter} - - Print - + Print {/if} {#if user.hasT1Review} @@ -47,9 +41,9 @@ {/if} {#if user.hasT2Review} - - YSWS Review - + + YSWS Review + {/if} {#if user.hasAdmin} diff --git a/src/routes/dashboard/SidebarButton.svelte b/src/routes/dashboard/SidebarButton.svelte index c183908..e740b4f 100644 --- a/src/routes/dashboard/SidebarButton.svelte +++ b/src/routes/dashboard/SidebarButton.svelte @@ -13,9 +13,10 @@ diff --git a/src/routes/dashboard/admin/admin/+page.svelte b/src/routes/dashboard/admin/admin/+page.svelte index 601dac5..e2e3394 100644 --- a/src/routes/dashboard/admin/admin/+page.svelte +++ b/src/routes/dashboard/admin/admin/+page.svelte @@ -1,6 +1,6 @@ @@ -47,5 +47,14 @@

    Item orders

    + +
    + +
    +

    Impersonate logs

    +
    diff --git a/src/routes/dashboard/admin/admin/impersonate-logs/+page.server.ts b/src/routes/dashboard/admin/admin/impersonate-logs/+page.server.ts new file mode 100644 index 0000000..ac7d1d8 --- /dev/null +++ b/src/routes/dashboard/admin/admin/impersonate-logs/+page.server.ts @@ -0,0 +1,55 @@ +import { db } from '$lib/server/db/index.js'; +import { impersonateAuditLog, user } from '$lib/server/db/schema.js'; +import { error } from '@sveltejs/kit'; +import { desc, eq, inArray } from 'drizzle-orm'; + +export async function load({ locals }) { + if (!locals.user) { + throw error(500); + } + if (!locals.user.hasAdmin) { + throw error(403, { message: 'oi get out' }); + } + + const logs = await db + .select({ + id: impersonateAuditLog.id, + timestamp: impersonateAuditLog.timestamp, + adminUser: { + id: user.id, + name: user.name, + profilePicture: user.profilePicture + }, + targetUserId: impersonateAuditLog.targetUserId + }) + .from(impersonateAuditLog) + .leftJoin(user, eq(impersonateAuditLog.adminUserId, user.id)) + .orderBy(desc(impersonateAuditLog.timestamp)); + + // Fetch target users in a single query + const targetUserIds = [...new Set(logs.map((log) => log.targetUserId))]; + const targetUsers = + targetUserIds.length > 0 + ? await db + .select({ + id: user.id, + name: user.name, + profilePicture: user.profilePicture + }) + .from(user) + .where(inArray(user.id, targetUserIds)) + : []; + + // Create a map for efficient lookup + const targetUserMap = new Map(targetUsers.map((u) => [u.id, u])); + + // Merge the data + const logsWithUsers = logs.map((log) => ({ + ...log, + targetUser: targetUserMap.get(log.targetUserId) + })); + + return { + logs: logsWithUsers + }; +} diff --git a/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte b/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte new file mode 100644 index 0000000..0ec407b --- /dev/null +++ b/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte @@ -0,0 +1,74 @@ + + + + +
    +

    Impersonate Audit Logs

    + +

    Showing {data.logs.length} impersonation events

    + + {#if data.logs.length == 0} +
    +
    +

    + No impersonation events found heavysob +

    +
    +
    + {:else} +
    + {#each data.logs as log (log.id)} +
    +
    + {log.adminUser?.name} + +
    + +
    + {log.targetUser?.name} +
    +

    Impersonated

    + + {log.targetUser?.name} + +
    +
    + +
    + + {relativeDate(log.timestamp)} + +
    +
    + {/each} +
    + {/if} +
    diff --git a/src/routes/dashboard/admin/admin/market/MarketItem.svelte b/src/routes/dashboard/admin/admin/market/MarketItem.svelte index 2e4a587..05bb715 100644 --- a/src/routes/dashboard/admin/admin/market/MarketItem.svelte +++ b/src/routes/dashboard/admin/admin/market/MarketItem.svelte @@ -14,7 +14,7 @@

    {#if showButtons} -
    + diff --git a/src/routes/dashboard/admin/admin/stats/+page.server.ts b/src/routes/dashboard/admin/admin/stats/+page.server.ts index 09cbd08..629993b 100644 --- a/src/routes/dashboard/admin/admin/stats/+page.server.ts +++ b/src/routes/dashboard/admin/admin/stats/+page.server.ts @@ -71,13 +71,15 @@ export async function load({ locals }) { .groupBy(project.id) .as('shippedProjects'); - const [shippedStats] = await db.select({ - count: count(), - totalTimeSpent: sql`sum(${shippedProjects.timeSpent})`, - averageTimeSpent: sql`avg(${shippedProjects.timeSpent})`, - totalDevlogs: sql`sum(${shippedProjects.devlogCount})`, - averageDevlogs: sql`avg(${shippedProjects.devlogCount})`, - }).from(shippedProjects); + const [shippedStats] = await db + .select({ + count: count(), + totalTimeSpent: sql`sum(${shippedProjects.timeSpent})`, + averageTimeSpent: sql`avg(${shippedProjects.timeSpent})`, + totalDevlogs: sql`sum(${shippedProjects.devlogCount})`, + averageDevlogs: sql`avg(${shippedProjects.devlogCount})` + }) + .from(shippedProjects); return { users: users, diff --git a/src/routes/dashboard/admin/admin/users/+page.server.ts b/src/routes/dashboard/admin/admin/users/+page.server.ts index e728b17..0b0492e 100644 --- a/src/routes/dashboard/admin/admin/users/+page.server.ts +++ b/src/routes/dashboard/admin/admin/users/+page.server.ts @@ -28,7 +28,7 @@ export const actions = { } await db.delete(session); - + return {}; } } satisfies Actions; diff --git a/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts b/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts index 554a98f..a83e6f3 100644 --- a/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts +++ b/src/routes/dashboard/admin/admin/users/[id]/+page.server.ts @@ -1,5 +1,5 @@ import { db } from '$lib/server/db/index.js'; -import { user, devlog, session } from '$lib/server/db/schema.js'; +import { user, devlog, session, impersonateAuditLog } from '$lib/server/db/schema.js'; import { error, fail, redirect } from '@sveltejs/kit'; import { and, eq, sql } from 'drizzle-orm'; import type { Actions } from './$types'; @@ -232,6 +232,12 @@ export const actions = { throw error(404, { message: 'user not found' }); } + // Log the impersonation + await db.insert(impersonateAuditLog).values({ + adminUserId: locals.user.id, + targetUserId: id + }); + const sessionToken = generateSessionToken(); await createSession(sessionToken, id); setSessionTokenCookie( diff --git a/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte b/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte index a0f433f..bdb8a0d 100644 --- a/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte +++ b/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte @@ -123,9 +123,7 @@
    {/if} -

    - YSWS Review -

    +

    YSWS Review

    Explore -
    {#if devlogs.length == 0}
    @@ -107,9 +106,7 @@ {#if loadError}
    {loadError} - +
    {/if} @@ -117,4 +114,4 @@

    You're caught up.

    {/if} {/if} -
    \ No newline at end of file +
    diff --git a/src/routes/dashboard/market/MarketTimer.svelte b/src/routes/dashboard/market/MarketTimer.svelte index 9aa1e6c..3538e3e 100644 --- a/src/routes/dashboard/market/MarketTimer.svelte +++ b/src/routes/dashboard/market/MarketTimer.svelte @@ -54,4 +54,4 @@ SECS
    - \ No newline at end of file + diff --git a/src/routes/dashboard/projects/+page.svelte b/src/routes/dashboard/projects/+page.svelte index d716df8..6945066 100644 --- a/src/routes/dashboard/projects/+page.svelte +++ b/src/routes/dashboard/projects/+page.svelte @@ -35,7 +35,7 @@ {:else} -
    +
    {#each data.projects as project} {data.project.name} -
    +

    Created @@ -109,7 +109,7 @@

    {/if} diff --git a/src/routes/dashboard/projects/[id]/delete/+page.server.ts b/src/routes/dashboard/projects/[id]/delete/+page.server.ts index 2855ef7..8c1a111 100644 --- a/src/routes/dashboard/projects/[id]/delete/+page.server.ts +++ b/src/routes/dashboard/projects/[id]/delete/+page.server.ts @@ -36,7 +36,14 @@ export async function load({ params, locals }) { or(eq(project.status, 'building'), eq(project.status, 'rejected')) ) ) - .groupBy(project.id, project.name, project.description, project.url, project.createdAt, project.status) + .groupBy( + project.id, + project.name, + project.description, + project.url, + project.createdAt, + project.status + ) .limit(1); if (!queriedProject) { @@ -87,7 +94,7 @@ export const actions = { eq(project.deleted, false) ) ); - + // Mark all associated devlogs as deleted await db .update(devlog) @@ -95,12 +102,7 @@ export const actions = { deleted: true, updatedAt: new Date(Date.now()) }) - .where( - and( - eq(devlog.projectId, queriedProject.id), - eq(devlog.userId, locals.user.id), - ) - ); + .where(and(eq(devlog.projectId, queriedProject.id), eq(devlog.userId, locals.user.id))); return redirect(303, '/dashboard/projects'); } diff --git a/src/routes/dashboard/projects/create/+page.svelte b/src/routes/dashboard/projects/create/+page.svelte index ae0afff..bfddeda 100644 --- a/src/routes/dashboard/projects/create/+page.svelte +++ b/src/routes/dashboard/projects/create/+page.svelte @@ -67,9 +67,7 @@
  • The number and quality of your devlogs
  • How polished your project is
  • -

    - A higher market score: -

    +

    A higher market score:

    • Reduces the price of market items
    • Lets you unlock more items in the market
    • @@ -83,7 +81,7 @@ { formPending = true; return async ({ update }) => { diff --git a/src/routes/dashboard/users/+page.server.ts b/src/routes/dashboard/users/+page.server.ts index 1fccc72..8f8ca5d 100644 --- a/src/routes/dashboard/users/+page.server.ts +++ b/src/routes/dashboard/users/+page.server.ts @@ -1,5 +1,5 @@ import { redirect } from '@sveltejs/kit'; export function load({ locals }) { - throw redirect(302, `/dashboard/users/${locals.user?.id}`); -}; \ No newline at end of file + throw redirect(302, `/dashboard/users/${locals.user?.id}`); +} diff --git a/wrangler.jsonc b/wrangler.jsonc index 67ccdf7..011237b 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -4,6 +4,6 @@ "compatibility_date": "2025-01-01", "assets": { "binding": "ASSETS", - "directory": ".svelte-kit/cloudflare", + "directory": ".svelte-kit/cloudflare" } -} \ No newline at end of file +} From 6e60d24b28d7aecf411e78c94581306c11b610dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 1 Jan 2026 15:54:55 +0000 Subject: [PATCH 3/4] Remove unrelated file changes, keep only impersonate audit logs Co-authored-by: ArcaEge <40526225+ArcaEge@users.noreply.github.com> --- .devcontainer/devcontainer.json | 6 +- .github/dependabot.yml | 8 +- .prettierrc | 5 +- docker-compose-staging.yaml | 2 +- docker-compose.yaml | 2 +- eslint.config.js | 14 +- package-lock.json | 28 ++- src/app.css | 10 +- src/lib/components/Button.svelte | 2 +- src/lib/components/OrpheusFlag.svelte | 1 + src/lib/components/Snowfall.svelte | 220 +++++++++--------- src/lib/defs.ts | 2 +- src/lib/server/airtable.ts | 4 +- src/lib/server/auth.ts | 3 +- src/lib/server/db/index.ts | 2 +- src/lib/server/db/schema.ts | 6 +- src/lib/server/slack.ts | 2 +- src/routes/+error.svelte | 12 +- src/routes/+layout.svelte | 2 +- src/routes/+page.server.ts | 2 +- src/routes/Footer.svelte | 5 +- src/routes/Rules.svelte | 19 +- src/routes/Shop.svelte | 6 +- src/routes/approved-editors/+page.svelte | 3 +- .../create-hackatime-account/+page.svelte | 16 +- src/routes/auth/failed/+page.svelte | 14 +- src/routes/auth/ineligible/+page.svelte | 16 +- src/routes/auth/logout/+server.ts | 2 +- src/routes/dashboard/+error.svelte | 6 +- src/routes/dashboard/+layout.svelte | 2 +- src/routes/dashboard/Sidebar.svelte | 14 +- src/routes/dashboard/SidebarButton.svelte | 5 +- .../admin/admin/market/MarketItem.svelte | 2 +- .../admin/admin/stats/+page.server.ts | 16 +- .../admin/admin/users/+page.server.ts | 2 +- .../admin/ysws-review/[id]/+page.svelte | 4 +- src/routes/dashboard/explore/+page.server.ts | 14 +- src/routes/dashboard/explore/+page.svelte | 7 +- .../dashboard/market/MarketTimer.svelte | 2 +- src/routes/dashboard/projects/+page.svelte | 2 +- .../dashboard/projects/[id]/+page.server.ts | 2 +- .../dashboard/projects/[id]/+page.svelte | 4 +- .../projects/[id]/delete/+page.server.ts | 18 +- .../dashboard/projects/create/+page.svelte | 6 +- src/routes/dashboard/users/+page.server.ts | 4 +- wrangler.jsonc | 4 +- 46 files changed, 279 insertions(+), 249 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e4e748f..fc2fe38 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,7 +10,9 @@ "ghcr.io/devcontainers/features/github-cli:1": {} }, // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [5173], + "forwardPorts": [ + 5173 + ], // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "npm install", // Configure tool-specific properties. @@ -28,4 +30,4 @@ } // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" -} +} \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3cddc75..f33a02c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,7 @@ version: 2 updates: - - package-ecosystem: 'devcontainers' - directory: '/' - schedule: - interval: weekly + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: weekly diff --git a/.prettierrc b/.prettierrc index 8103a0b..8855237 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,7 +3,10 @@ "singleQuote": true, "trailingComma": "none", "printWidth": 100, - "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "plugins": [ + "prettier-plugin-svelte", + "prettier-plugin-tailwindcss" + ], "overrides": [ { "files": "*.svelte", diff --git a/docker-compose-staging.yaml b/docker-compose-staging.yaml index 5186ed1..e2f656b 100644 --- a/docker-compose-staging.yaml +++ b/docker-compose-staging.yaml @@ -18,4 +18,4 @@ services: image: ghcr.io/hackclub/construct:staging networks: {} volumes: - constructdb: + constructdb: \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 8d67fc0..6497457 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -18,4 +18,4 @@ services: image: ghcr.io/hackclub/construct:production networks: {} volumes: - constructdb: + constructdb: \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 2c49fa6..e78afbd 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -21,14 +21,16 @@ export default defineConfig( languageOptions: { globals: { ...globals.browser, ...globals.node } }, - rules: { - // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. - // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - 'no-undef': 'off' - } + rules: { // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + "no-undef": 'off' } }, { - files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], + files: [ + '**/*.svelte', + '**/*.svelte.ts', + '**/*.svelte.js' + ], languageOptions: { parserOptions: { projectService: true, diff --git a/package-lock.json b/package-lock.json index 626ab37..f6c020c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -984,6 +984,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3027,6 +3028,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -3048,6 +3050,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.19.0 || >=20.6.0" }, @@ -3060,6 +3063,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -3075,6 +3079,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz", "integrity": "sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.208.0", "import-in-the-middle": "^2.0.0", @@ -3462,6 +3467,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" @@ -3478,6 +3484,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", @@ -3495,6 +3502,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -5301,6 +5309,7 @@ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.47.0.tgz", "integrity": "sha512-mznN01MBXtr4T7X/E3ENkhF6GzqxTxL6/whG3OzCzUu8G8KYRNiCdoxLMVWAHJx/mDMPP3XAeKCMZHF/Xd/CDw==", "license": "MIT", + "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", @@ -5339,6 +5348,7 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "license": "MIT", + "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", @@ -5845,6 +5855,7 @@ "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.1", "@typescript-eslint/types": "8.46.1", @@ -6100,6 +6111,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6376,6 +6388,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -6989,6 +7002,7 @@ "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -7085,6 +7099,7 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -9384,6 +9399,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -9505,6 +9521,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -9677,6 +9694,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9693,6 +9711,7 @@ "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" @@ -9998,6 +10017,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -10575,6 +10595,7 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.40.1.tgz", "integrity": "sha512-0R3t2oiLxJNJb2buz61MNfPdkjeyj2qTCM7TtIv/4ZfF12zD7Ig8iIo+C8febroy+9S4QJ7qfijtearSdO/1ww==", "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -10667,7 +10688,8 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.14.tgz", "integrity": "sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.0", @@ -10832,6 +10854,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10890,6 +10913,7 @@ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.21.tgz", "integrity": "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==", "license": "MIT", + "peer": true, "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.7", @@ -11040,6 +11064,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -11615,6 +11640,7 @@ "integrity": "sha512-HwaJmXO3M1r4S8x2ea2vy8Rw/y/38HRQuK/gNDRQ7w9cJXn6xSl1sIIqKCffULSUjul3wV3I3Nd/GfbmsRReEA==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, diff --git a/src/app.css b/src/app.css index 0dcd37b..b1dd974 100644 --- a/src/app.css +++ b/src/app.css @@ -254,19 +254,19 @@ } .themed-input-light { - @apply border-3 border-primary-700 bg-primary-900 fill-primary-50 p-2 text-sm ring-primary-900 outline-transparent placeholder:text-primary-700 active:ring-3; + @apply border-3 border-primary-700 bg-primary-900 fill-primary-50 p-2 text-sm ring-primary-900 placeholder:text-primary-700 active:ring-3 outline-transparent; } .themed-input { - @apply rounded-lg border-3 border-primary-900 bg-primary-950 ring-primary-700 outline-transparent transition-shadow placeholder:text-primary-800 focus:ring-2; + @apply bg-primary-950 border-3 border-primary-900 ring-primary-700 focus:ring-2 transition-shadow rounded-lg placeholder:text-primary-800 outline-transparent; } .themed-input-on-box { - @apply rounded-lg border-3 border-primary-800 bg-primary-900 ring-primary-700 outline-transparent transition-shadow placeholder:text-primary-700 focus:ring-2; + @apply bg-primary-900 border-3 border-primary-800 ring-primary-700 focus:ring-2 transition-shadow rounded-lg placeholder:text-primary-700 outline-transparent; } .checkbox { - @apply h-4 w-4 rounded-sm border-2 border-primary-600 bg-primary-900 ring-0; + @apply border-2 border-primary-600 bg-primary-900 ring-0 h-4 w-4 rounded-sm; } } @@ -276,6 +276,6 @@ } .center { - @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform; + @apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2; } } diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte index be1604b..f693cc4 100644 --- a/src/lib/components/Button.svelte +++ b/src/lib/components/Button.svelte @@ -11,7 +11,7 @@ diff --git a/src/lib/components/OrpheusFlag.svelte b/src/lib/components/OrpheusFlag.svelte index 9ccf5cc..7ddb956 100644 --- a/src/lib/components/OrpheusFlag.svelte +++ b/src/lib/components/OrpheusFlag.svelte @@ -14,3 +14,4 @@ alt="Hack Club" /> + diff --git a/src/lib/components/Snowfall.svelte b/src/lib/components/Snowfall.svelte index 18080a5..3b83393 100644 --- a/src/lib/components/Snowfall.svelte +++ b/src/lib/components/Snowfall.svelte @@ -1,129 +1,127 @@ - - + + diff --git a/src/lib/defs.ts b/src/lib/defs.ts index 76e1bb5..d3b5824 100644 --- a/src/lib/defs.ts +++ b/src/lib/defs.ts @@ -15,4 +15,4 @@ export const PRINT_PAYOUT_CENTS_PER_GRAM = 2.0; export const PRINT_MINUTES_PER_GRAM = 0.24; export const BETA_DATE_CUTOFF = new Date('2025-12-16'); -export const BETA_MULTIPLIER = 1.5; +export const BETA_MULTIPLIER = 1.5; \ No newline at end of file diff --git a/src/lib/server/airtable.ts b/src/lib/server/airtable.ts index ce5914c..d84b547 100644 --- a/src/lib/server/airtable.ts +++ b/src/lib/server/airtable.ts @@ -1,6 +1,4 @@ import Airtable from 'airtable'; import { env } from '$env/dynamic/private'; -export const airtableBase = env.AIRTABLE_TOKEN - ? new Airtable({ apiKey: env.AIRTABLE_TOKEN }).base(env.AIRTABLE_BASE) - : null; +export const airtableBase = env.AIRTABLE_TOKEN ? new Airtable({ apiKey: env.AIRTABLE_TOKEN }).base(env.AIRTABLE_BASE) : null; diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index c1120e6..2e4b954 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -49,8 +49,7 @@ export async function validateSessionToken(token: string) { return { session: null, user: null }; } - const renewSession = - Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * (SESSION_EXPIRY_DAYS / 2); + const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * (SESSION_EXPIRY_DAYS / 2); if (renewSession) { session.expiresAt = new Date(Date.now() + DAY_IN_MS * SESSION_EXPIRY_DAYS); await db diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts index af71f88..cc98b8d 100644 --- a/src/lib/server/db/index.ts +++ b/src/lib/server/db/index.ts @@ -3,7 +3,7 @@ import { Pool } from 'pg'; import * as schema from './schema'; const pool = new Pool({ - connectionString: process.env.DATABASE_URL! + connectionString: process.env.DATABASE_URL!, }); export const db = drizzle(pool, { schema }); diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 5115085..5065872 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -237,16 +237,14 @@ export const marketOrderStatus = pgEnum('market_order_status', [ export const marketItemOrder = pgTable('market_item_order', { id: serial().primaryKey(), - userId: integer() - .references(() => user.id) - .notNull(), + userId: integer().references(() => user.id).notNull(), addressId: text().notNull(), bricksPaid: integer().notNull(), status: marketOrderStatus().notNull().default('awaiting_approval'), userNotes: text().notNull(), - notes: text(), // stuff like tracking code, shown to user + notes: text(), // stuff like tracking code, shown to user deleted: boolean().notNull().default(false), createdAt: timestamp().notNull().defaultNow() diff --git a/src/lib/server/slack.ts b/src/lib/server/slack.ts index 12962e6..dfc0d4e 100644 --- a/src/lib/server/slack.ts +++ b/src/lib/server/slack.ts @@ -13,7 +13,7 @@ export async function sendSlackDM(userId: string, message: string) { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` + 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ channel: userId, diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte index dd58336..c378729 100644 --- a/src/routes/+error.svelte +++ b/src/routes/+error.svelte @@ -2,16 +2,10 @@ import { page } from '$app/state'; -
      +

      oh no!!!

      Error {page.status}

      {page.error?.message}

      - {`error - Go back home + {`error + Go back home
      diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index eeeba66..e45cb52 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -23,4 +23,4 @@ :global(body) { position: relative; } - + \ No newline at end of file diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index d175818..cb20996 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -3,6 +3,6 @@ import { env } from '$env/dynamic/private'; export function load({ locals }) { return { loggedIn: locals.session !== null, - idvDomain: env.IDV_DOMAIN + idvDomain: env.IDV_DOMAIN, }; } diff --git a/src/routes/Footer.svelte b/src/routes/Footer.svelte index f7bf211..b411335 100644 --- a/src/routes/Footer.svelte +++ b/src/routes/Footer.svelte @@ -8,10 +8,7 @@ A Hack Club program. Made by teens, for teens! {#if env.PUBLIC_COMMIT} - · Build {env.PUBLIC_COMMIT.slice(0, 7)} + · Build {env.PUBLIC_COMMIT.slice(0, 7)} {/if}

      diff --git a/src/routes/Rules.svelte b/src/routes/Rules.svelte index cf3f7b3..a79d34e 100644 --- a/src/routes/Rules.svelte +++ b/src/routes/Rules.svelte @@ -11,7 +11,10 @@
    • You must be 13-18 years old.
    • You cannot be banned from Hackatime.
    • - You must be part of the + You must be part of the Hack Club Slack community .
    • @@ -30,13 +33,15 @@
      • You should spend at least 2 hours on each individual project.
      • - You should use one of our approved editors. + You should use one of our approved editors. +
      • +
      • + You should track your time with the journaling feature. +
      • +
      • + You should upload your project to Printables once done.
      • -
      • You should track your time with the journaling feature.
      • -
      • You should upload your project to Printables once done.
    -
    +
    \ No newline at end of file diff --git a/src/routes/Shop.svelte b/src/routes/Shop.svelte index d719baf..419c97d 100644 --- a/src/routes/Shop.svelte +++ b/src/routes/Shop.svelte @@ -20,7 +20,11 @@ text="Cool rocket but there are better ones ∙ 15 hours" imgsrc={prize02} /> - +

    ...and more!

    diff --git a/src/routes/approved-editors/+page.svelte b/src/routes/approved-editors/+page.svelte index 555dd8c..2e4effd 100644 --- a/src/routes/approved-editors/+page.svelte +++ b/src/routes/approved-editors/+page.svelte @@ -46,8 +46,7 @@

    Want to get something else added here? Ask in #construct-help#construct-help!

    diff --git a/src/routes/auth/create-hackatime-account/+page.svelte b/src/routes/auth/create-hackatime-account/+page.svelte index 2e73338..62d3c53 100644 --- a/src/routes/auth/create-hackatime-account/+page.svelte +++ b/src/routes/auth/create-hackatime-account/+page.svelte @@ -1,13 +1,13 @@ -
    -

    Hackatime account not found

    - -

    Make sure you have a Hackatime account associated with your Slack account.

    - Make one? - Try again -
    +
    +

    Hackatime account not found

    + +

    Make sure you have a Hackatime account associated with your Slack account.

    + Make one? + Try again +
    \ No newline at end of file diff --git a/src/routes/auth/failed/+page.svelte b/src/routes/auth/failed/+page.svelte index 8186463..15ec7c7 100644 --- a/src/routes/auth/failed/+page.svelte +++ b/src/routes/auth/failed/+page.svelte @@ -1,12 +1,12 @@ -
    -

    Authentication failed

    - -

    Authentication failed.

    - Try again? -
    +
    +

    Authentication failed

    + +

    Authentication failed.

    + Try again? +
    \ No newline at end of file diff --git a/src/routes/auth/ineligible/+page.svelte b/src/routes/auth/ineligible/+page.svelte index 2ffb081..d882dad 100644 --- a/src/routes/auth/ineligible/+page.svelte +++ b/src/routes/auth/ineligible/+page.svelte @@ -1,14 +1,14 @@ -
    -

    Ineligible

    - -

    Sorry, you're ineligible to take part in this event until you get verified.

    - Get verified -
    +
    +

    Ineligible

    + +

    Sorry, you're ineligible to take part in this event until you get verified.

    + Get verified +
    \ No newline at end of file diff --git a/src/routes/auth/logout/+server.ts b/src/routes/auth/logout/+server.ts index bd7c790..0f94be2 100644 --- a/src/routes/auth/logout/+server.ts +++ b/src/routes/auth/logout/+server.ts @@ -9,5 +9,5 @@ export async function GET(event) { deleteSessionTokenCookie(event); } - return redirect(302, '/'); + return redirect(302, "/"); } diff --git a/src/routes/dashboard/+error.svelte b/src/routes/dashboard/+error.svelte index b890f6b..c1c00a2 100644 --- a/src/routes/dashboard/+error.svelte +++ b/src/routes/dashboard/+error.svelte @@ -8,10 +8,6 @@

    oh no!!!

    Error {page.status}

    {page.error?.message}

    - {`error + {`error Go back home diff --git a/src/routes/dashboard/+layout.svelte b/src/routes/dashboard/+layout.svelte index 5448dd9..fcbefe3 100644 --- a/src/routes/dashboard/+layout.svelte +++ b/src/routes/dashboard/+layout.svelte @@ -1,6 +1,6 @@ diff --git a/src/routes/dashboard/Sidebar.svelte b/src/routes/dashboard/Sidebar.svelte index 750c02a..be067c1 100644 --- a/src/routes/dashboard/Sidebar.svelte +++ b/src/routes/dashboard/Sidebar.svelte @@ -8,8 +8,12 @@ ClipboardPen, ClipboardPenLine, Store, + ShieldUser, + Box + + } from '@lucide/svelte'; import { page } from '$app/state'; import logo from '$lib/assets/logo.png'; @@ -33,7 +37,9 @@ Explore Market {#if user.isPrinter} - Print + + Print + {/if} {#if user.hasT1Review} @@ -41,9 +47,9 @@ {/if} {#if user.hasT2Review} - - YSWS Review - + + YSWS Review + {/if} {#if user.hasAdmin} diff --git a/src/routes/dashboard/SidebarButton.svelte b/src/routes/dashboard/SidebarButton.svelte index e740b4f..c183908 100644 --- a/src/routes/dashboard/SidebarButton.svelte +++ b/src/routes/dashboard/SidebarButton.svelte @@ -13,10 +13,9 @@ diff --git a/src/routes/dashboard/admin/admin/market/MarketItem.svelte b/src/routes/dashboard/admin/admin/market/MarketItem.svelte index 05bb715..2e4a587 100644 --- a/src/routes/dashboard/admin/admin/market/MarketItem.svelte +++ b/src/routes/dashboard/admin/admin/market/MarketItem.svelte @@ -14,7 +14,7 @@

    {#if showButtons} -
    + diff --git a/src/routes/dashboard/admin/admin/stats/+page.server.ts b/src/routes/dashboard/admin/admin/stats/+page.server.ts index 629993b..09cbd08 100644 --- a/src/routes/dashboard/admin/admin/stats/+page.server.ts +++ b/src/routes/dashboard/admin/admin/stats/+page.server.ts @@ -71,15 +71,13 @@ export async function load({ locals }) { .groupBy(project.id) .as('shippedProjects'); - const [shippedStats] = await db - .select({ - count: count(), - totalTimeSpent: sql`sum(${shippedProjects.timeSpent})`, - averageTimeSpent: sql`avg(${shippedProjects.timeSpent})`, - totalDevlogs: sql`sum(${shippedProjects.devlogCount})`, - averageDevlogs: sql`avg(${shippedProjects.devlogCount})` - }) - .from(shippedProjects); + const [shippedStats] = await db.select({ + count: count(), + totalTimeSpent: sql`sum(${shippedProjects.timeSpent})`, + averageTimeSpent: sql`avg(${shippedProjects.timeSpent})`, + totalDevlogs: sql`sum(${shippedProjects.devlogCount})`, + averageDevlogs: sql`avg(${shippedProjects.devlogCount})`, + }).from(shippedProjects); return { users: users, diff --git a/src/routes/dashboard/admin/admin/users/+page.server.ts b/src/routes/dashboard/admin/admin/users/+page.server.ts index 0b0492e..e728b17 100644 --- a/src/routes/dashboard/admin/admin/users/+page.server.ts +++ b/src/routes/dashboard/admin/admin/users/+page.server.ts @@ -28,7 +28,7 @@ export const actions = { } await db.delete(session); - + return {}; } } satisfies Actions; diff --git a/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte b/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte index bdb8a0d..a0f433f 100644 --- a/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte +++ b/src/routes/dashboard/admin/ysws-review/[id]/+page.svelte @@ -123,7 +123,9 @@
    {/if} -

    YSWS Review

    +

    + YSWS Review +

    Explore +
    {#if devlogs.length == 0}
    @@ -106,7 +107,9 @@ {#if loadError}
    {loadError} - +
    {/if} @@ -114,4 +117,4 @@

    You're caught up.

    {/if} {/if} -
    +
    \ No newline at end of file diff --git a/src/routes/dashboard/market/MarketTimer.svelte b/src/routes/dashboard/market/MarketTimer.svelte index 3538e3e..9aa1e6c 100644 --- a/src/routes/dashboard/market/MarketTimer.svelte +++ b/src/routes/dashboard/market/MarketTimer.svelte @@ -54,4 +54,4 @@ SECS
    - + \ No newline at end of file diff --git a/src/routes/dashboard/projects/+page.svelte b/src/routes/dashboard/projects/+page.svelte index 6945066..d716df8 100644 --- a/src/routes/dashboard/projects/+page.svelte +++ b/src/routes/dashboard/projects/+page.svelte @@ -35,7 +35,7 @@ {:else} -
    +
    {#each data.projects as project} {data.project.name} -
    +

    Created @@ -109,7 +109,7 @@

    {/if} diff --git a/src/routes/dashboard/projects/[id]/delete/+page.server.ts b/src/routes/dashboard/projects/[id]/delete/+page.server.ts index 8c1a111..2855ef7 100644 --- a/src/routes/dashboard/projects/[id]/delete/+page.server.ts +++ b/src/routes/dashboard/projects/[id]/delete/+page.server.ts @@ -36,14 +36,7 @@ export async function load({ params, locals }) { or(eq(project.status, 'building'), eq(project.status, 'rejected')) ) ) - .groupBy( - project.id, - project.name, - project.description, - project.url, - project.createdAt, - project.status - ) + .groupBy(project.id, project.name, project.description, project.url, project.createdAt, project.status) .limit(1); if (!queriedProject) { @@ -94,7 +87,7 @@ export const actions = { eq(project.deleted, false) ) ); - + // Mark all associated devlogs as deleted await db .update(devlog) @@ -102,7 +95,12 @@ export const actions = { deleted: true, updatedAt: new Date(Date.now()) }) - .where(and(eq(devlog.projectId, queriedProject.id), eq(devlog.userId, locals.user.id))); + .where( + and( + eq(devlog.projectId, queriedProject.id), + eq(devlog.userId, locals.user.id), + ) + ); return redirect(303, '/dashboard/projects'); } diff --git a/src/routes/dashboard/projects/create/+page.svelte b/src/routes/dashboard/projects/create/+page.svelte index bfddeda..ae0afff 100644 --- a/src/routes/dashboard/projects/create/+page.svelte +++ b/src/routes/dashboard/projects/create/+page.svelte @@ -67,7 +67,9 @@
  • The number and quality of your devlogs
  • How polished your project is
  • -

    A higher market score:

    +

    + A higher market score: +

    • Reduces the price of market items
    • Lets you unlock more items in the market
    • @@ -81,7 +83,7 @@ { formPending = true; return async ({ update }) => { diff --git a/src/routes/dashboard/users/+page.server.ts b/src/routes/dashboard/users/+page.server.ts index 8f8ca5d..1fccc72 100644 --- a/src/routes/dashboard/users/+page.server.ts +++ b/src/routes/dashboard/users/+page.server.ts @@ -1,5 +1,5 @@ import { redirect } from '@sveltejs/kit'; export function load({ locals }) { - throw redirect(302, `/dashboard/users/${locals.user?.id}`); -} + throw redirect(302, `/dashboard/users/${locals.user?.id}`); +}; \ No newline at end of file diff --git a/wrangler.jsonc b/wrangler.jsonc index 011237b..67ccdf7 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -4,6 +4,6 @@ "compatibility_date": "2025-01-01", "assets": { "binding": "ASSETS", - "directory": ".svelte-kit/cloudflare" + "directory": ".svelte-kit/cloudflare", } -} +} \ No newline at end of file From b1009625b299656676c02a90e3ec79895d82f5ec Mon Sep 17 00:00:00 2001 From: Arca Ege Cengiz Date: Thu, 1 Jan 2026 18:15:20 +0000 Subject: [PATCH 4/4] Update impersonate and user pages --- .../dashboard/admin/admin/impersonate-logs/+page.svelte | 2 +- src/routes/dashboard/admin/admin/users/+page.svelte | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte b/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte index 0ec407b..d063d21 100644 --- a/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte +++ b/src/routes/dashboard/admin/admin/impersonate-logs/+page.svelte @@ -62,7 +62,7 @@
    -
    +
    {relativeDate(log.timestamp)} diff --git a/src/routes/dashboard/admin/admin/users/+page.svelte b/src/routes/dashboard/admin/admin/users/+page.svelte index 455aab6..33f6709 100644 --- a/src/routes/dashboard/admin/admin/users/+page.svelte +++ b/src/routes/dashboard/admin/admin/users/+page.svelte @@ -89,7 +89,11 @@

    Hackatime: {user.hackatimeTrust}

    Trust: {user.trust}

    -

    {user.clay} clay, {user.brick} brick, {user.shopScore} market

    +

    + {Math.round(user.clay * 10) / 10} clay, {Math.round(user.brick * 10) / 10} brick, {Math.round( + user.shopScore * 10 + ) / 10} market +

    {/each}