mirror of
https://github.com/tale/headplane.git
synced 2026-08-28 07:57:03 +00:00
fix: store profile picture in db to prevent header overload
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Form, redirect, useSearchParams } from "react-router";
|
import { Form, Link as RouterLink, redirect, useSearchParams } from "react-router";
|
||||||
|
|
||||||
import Button from "~/components/button";
|
import Button from "~/components/button";
|
||||||
import Card from "~/components/card";
|
import Card from "~/components/card";
|
||||||
@@ -139,11 +139,11 @@ export default function Page({ loaderData, actionData }: Route.ComponentProps) {
|
|||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
{isOidcConnectorEnabled ? (
|
{isOidcConnectorEnabled ? (
|
||||||
<Link to="/oidc/start">
|
<RouterLink to="/oidc/start" prefetch="none" reloadDocument>
|
||||||
<Button className="mt-2 w-full" disabled={oidcErrorCodes.length > 0} variant="light">
|
<Button className="mt-2 w-full" disabled={oidcErrorCodes.length > 0} variant="light">
|
||||||
Single Sign-On
|
Single Sign-On
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</RouterLink>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
|||||||
const userId = await context.auth.findOrCreateUser(claims.sub, {
|
const userId = await context.auth.findOrCreateUser(claims.sub, {
|
||||||
name,
|
name,
|
||||||
email: userInfo.email,
|
email: userInfo.email,
|
||||||
|
picture,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -122,7 +123,6 @@ export async function loader({ request, context }: Route.LoaderArgs) {
|
|||||||
name,
|
name,
|
||||||
email: userInfo.email,
|
email: userInfo.email,
|
||||||
username,
|
username,
|
||||||
picture,
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export const users = sqliteTable("users", {
|
|||||||
sub: text("sub").notNull().unique(),
|
sub: text("sub").notNull().unique(),
|
||||||
name: text("name"),
|
name: text("name"),
|
||||||
email: text("email"),
|
email: text("email"),
|
||||||
|
picture: text("picture"),
|
||||||
role: text("role").notNull().default("member"),
|
role: text("role").notNull().default("member"),
|
||||||
headscale_user_id: text("headscale_user_id").unique(),
|
headscale_user_id: text("headscale_user_id").unique(),
|
||||||
created_at: integer("created_at", { mode: "timestamp" }).$default(() => new Date()),
|
created_at: integer("created_at", { mode: "timestamp" }).$default(() => new Date()),
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ interface CookiePayload {
|
|||||||
name: string;
|
name: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
picture?: string;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,9 +148,11 @@ export class AuthService {
|
|||||||
role,
|
role,
|
||||||
headscaleUserId: user.headscale_user_id ?? undefined,
|
headscaleUserId: user.headscale_user_id ?? undefined,
|
||||||
},
|
},
|
||||||
profile: payload.profile ?? {
|
profile: {
|
||||||
name: user.name ?? user.sub,
|
name: payload.profile?.name ?? user.name ?? user.sub,
|
||||||
email: user.email ?? undefined,
|
email: payload.profile?.email ?? user.email ?? undefined,
|
||||||
|
username: payload.profile?.username,
|
||||||
|
picture: user.picture ?? undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -278,7 +279,7 @@ export class AuthService {
|
|||||||
*/
|
*/
|
||||||
async findOrCreateUser(
|
async findOrCreateUser(
|
||||||
subject: string,
|
subject: string,
|
||||||
profile?: { name?: string; email?: string },
|
profile?: { name?: string; email?: string; picture?: string },
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const [existing] = await this.opts.db
|
const [existing] = await this.opts.db
|
||||||
.select()
|
.select()
|
||||||
@@ -292,6 +293,7 @@ export class AuthService {
|
|||||||
.set({
|
.set({
|
||||||
name: profile?.name,
|
name: profile?.name,
|
||||||
email: profile?.email,
|
email: profile?.email,
|
||||||
|
picture: profile?.picture,
|
||||||
last_login_at: new Date(),
|
last_login_at: new Date(),
|
||||||
updated_at: new Date(),
|
updated_at: new Date(),
|
||||||
})
|
})
|
||||||
@@ -305,6 +307,7 @@ export class AuthService {
|
|||||||
sub: subject,
|
sub: subject,
|
||||||
name: profile?.name,
|
name: profile?.name,
|
||||||
email: profile?.email,
|
email: profile?.email,
|
||||||
|
picture: profile?.picture,
|
||||||
role: "member",
|
role: "member",
|
||||||
caps: capsForRole("member"),
|
caps: capsForRole("member"),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
ALTER TABLE `users` ADD `picture` text;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_users` (
|
||||||
|
`id` text PRIMARY KEY,
|
||||||
|
`sub` text NOT NULL UNIQUE,
|
||||||
|
`name` text,
|
||||||
|
`email` text,
|
||||||
|
`picture` text,
|
||||||
|
`role` text DEFAULT 'member' NOT NULL,
|
||||||
|
`headscale_user_id` text UNIQUE,
|
||||||
|
`created_at` integer,
|
||||||
|
`updated_at` integer,
|
||||||
|
`last_login_at` integer,
|
||||||
|
`caps` integer DEFAULT 0 NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_users`(`id`, `sub`, `name`, `email`, `role`, `headscale_user_id`, `created_at`, `updated_at`, `last_login_at`, `caps`) SELECT `id`, `sub`, `name`, `email`, `role`, `headscale_user_id`, `created_at`, `updated_at`, `last_login_at`, `caps` FROM `users`;--> statement-breakpoint
|
||||||
|
DROP TABLE `users`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_users` RENAME TO `users`;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=ON;--> statement-breakpoint
|
||||||
|
DROP INDEX IF EXISTS `users_sub_unique`;--> statement-breakpoint
|
||||||
|
DROP INDEX IF EXISTS `users_headscale_user_id_unique`;
|
||||||
@@ -0,0 +1,297 @@
|
|||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"id": "b0533d83-6eb6-422f-925f-c59e4db89384",
|
||||||
|
"prevIds": ["6d6838a7-e7ab-4661-bc7d-b47566bfff13"],
|
||||||
|
"ddl": [
|
||||||
|
{
|
||||||
|
"name": "auth_sessions",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ephemeral_nodes",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "host_info",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "users",
|
||||||
|
"entityType": "tables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "kind",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "user_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "api_key_hash",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "api_key_display",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "expires_at",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "created_at",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "auth_sessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "auth_key",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "ephemeral_nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "node_key",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "ephemeral_nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "host_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "host_info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "payload",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "host_info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "updated_at",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "host_info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "sub",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "name",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "email",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "picture",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'member'",
|
||||||
|
"generated": null,
|
||||||
|
"name": "role",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "headscale_user_id",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "created_at",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "updated_at",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": null,
|
||||||
|
"generated": null,
|
||||||
|
"name": "last_login_at",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "0",
|
||||||
|
"generated": null,
|
||||||
|
"name": "caps",
|
||||||
|
"entityType": "columns",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "auth_sessions_pk",
|
||||||
|
"table": "auth_sessions",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["auth_key"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "ephemeral_nodes_pk",
|
||||||
|
"table": "ephemeral_nodes",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["host_id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "host_info_pk",
|
||||||
|
"table": "host_info",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "users_pk",
|
||||||
|
"table": "users",
|
||||||
|
"entityType": "pks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["sub"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "users_sub_unique",
|
||||||
|
"entityType": "uniques",
|
||||||
|
"table": "users"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": ["headscale_user_id"],
|
||||||
|
"nameExplicit": false,
|
||||||
|
"name": "users_headscale_user_id_unique",
|
||||||
|
"entityType": "uniques",
|
||||||
|
"table": "users"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"renames": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user