Store Google profile picture bytes on google_auth

This commit is contained in:
seb
2026-08-05 11:08:06 +02:00
parent d1eb2716ae
commit 561ba5bb99
9 changed files with 859 additions and 812 deletions

View File

@@ -71,17 +71,19 @@ For `PATCH /api/company/:id`:
| Method | Signature | Returns | | Method | Signature | Returns |
| --- | --- | --- | | --- | --- | --- |
| `add` | `(email)` | `id` | | `add` | `(email)` | `id` |
| `getByEmail` | `(email)` | `{ id, email, picture, sub, createdAt, selectedCompanyId } \| undefined` | | `getByEmail` | `(email)` | `{ id, email, picture, pictureBinary, sub, createdAt, selectedCompanyId } \| undefined` |
| `listAllowedEmails` | `()` | `{ id, email, picture, sub, createdAt, selectedCompanyId }[]` | | `getById` | `(googleAuthId)` | `{ id, email, picture, pictureBinary, sub, createdAt, selectedCompanyId } \| undefined` |
| `listAllowedEmails` | `()` | `{ id, email, picture, sub, createdAt, selectedCompanyId }[]` (no `pictureBinary`) |
| `removeAllowedEmail` | `(email)` | `void` | | `removeAllowedEmail` | `(email)` | `void` |
| `setSelectedCompany` | `(googleAuthId, companyId)` | `void` | | `setSelectedCompany` | `(googleAuthId, companyId)` | `void` |
| `setProfile` | `(googleAuthId, { picture?: string \| null, sub?: string \| null })` | `void` | | `setProfile` | `(googleAuthId, { picture?: string \| null, pictureBinary?: Buffer \| null, sub?: string \| null })` | `void` |
`picture` / `sub` come from the Google ID token (`payload.picture`, `payload.sub`). `sub` is Google’s stable subject id (unique when set). `setProfile` overwrites both columns (pass `null` to clear). Errors: `User not found`. `picture` is the Google profile image URL (kept for change detection / re-fetch). `picturebinary` stores the downloaded image bytes (`pictureBinary` in JS, `Buffer` from mssql). `sub` is Google’s stable subject id (unique when set). `setProfile` overwrites `picture`, `pictureBinary`, and `sub` (pass `null` to clear). Non-Buffer `pictureBinary` → `Invalid pictureBinary`. Errors: `User not found`.
Backend mapping: Backend mapping:
- Login: `getByEmail(email)` then `setProfile(id, { picture, sub })` from the verified token - Login: `getByEmail(email)` then download `payload.picture` → `setProfile(id, { picture, pictureBinary, sub })`
- `/auth/me`: `getById(googleAuthId)` → base64-encode `pictureBinary` for the response
- CLI user manager: `listAllowedEmails()` / `removeAllowedEmail(email)` / `add(email)` - CLI user manager: `listAllowedEmails()` / `removeAllowedEmail(email)` / `add(email)`
- `PUT /api/auth/selected-company`: `setSelectedCompany(googleAuthId, companyId)` - `PUT /api/auth/selected-company`: `setSelectedCompany(googleAuthId, companyId)`

View File

@@ -17,7 +17,7 @@ digraph schema {
meta [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>meta</B></FONT></TD></TR><TR><TD PORT="key" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">key</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="value" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">value</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR></TABLE>>]; meta [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>meta</B></FONT></TD></TR><TR><TD PORT="key" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">key</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="value" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">value</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
customergroup [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in1" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in2" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="3" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>customergroup</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="company_id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>company_id</B>?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="name" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">name</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR></TABLE>>]; customergroup [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in1" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD><TD PORT="in2" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="3" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>customergroup</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="company_id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>company_id</B>?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="name" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">name</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
companysetting [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>companysetting</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="company_id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>company_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="key" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">key</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="value" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">value</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="frontend" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">frontend</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">BIT</FONT></TD></TR></TABLE></TD></TR></TABLE>>]; companysetting [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="4" BORDER="1"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>companysetting</B></FONT></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="company_id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>company_id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="key" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">key</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="value" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">value</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="frontend" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">frontend</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">BIT</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
google_auth [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="1" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>google_auth</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="selected_company" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>selected_company</B>?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="email" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">email</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(320)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="picture" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">picture?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(2048)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sub" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">sub?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="created_at" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">created_at</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">DATETIME2</FONT></TD></TR></TABLE></TD></TR></TABLE>>]; google_auth [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8" ALIGN="CENTER" CELLPADDING="1" BORDER="1"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD PORT="in0" BGCOLOR="#bee3f8" BORDER="0" WIDTH="10" HEIGHT="4" CELLPADDING="0"></TD></TR><TR><TD COLSPAN="1" ALIGN="CENTER" CELLPADDING="3"><FONT COLOR="#1a365d" POINT-SIZE="12"><B>google_auth</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="id" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>id</B></FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>PK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="selected_company" COLSPAN="2" BGCOLOR="#ebf8ff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748"><B>selected_company</B>?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">INT <B>FK</B></FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="email" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">email</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(320)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="picture" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">picture?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(2048)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="picturebinary" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">picturebinary?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">VARBINARY(MAX)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="sub" COLSPAN="2" BGCOLOR="#f7fafc" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">sub?</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">NVARCHAR(255)</FONT></TD></TR></TABLE></TD></TR><TR><TD PORT="created_at" COLSPAN="2" BGCOLOR="#ffffff" BORDER="1" CELLPADDING="0"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" FIXEDSIZE="FALSE"><TR><TD ALIGN="LEFT" CELLPADDING="4"><FONT COLOR="#2d3748">created_at</FONT></TD><TD ALIGN="RIGHT" CELLPADDING="4"><FONT COLOR="#718096" POINT-SIZE="9">DATETIME2</FONT></TD></TR></TABLE></TD></TR></TABLE>>];
} }
subgraph cluster_i18n { subgraph cluster_i18n {
label="Localization"; labeljust="l"; fontsize=15; label="Localization"; labeljust="l"; fontsize=15;
@@ -72,7 +72,7 @@ digraph schema {
__legend [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0" BGCOLOR="#ffffff"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8"><FONT COLOR="#1a365d" POINT-SIZE="11"><B>Legend</B></FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#2d3748"><B>PK</B></FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">primary key</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#2d3748"><B>FK</B></FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">foreign key</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#2d3748"><B>?</B></FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">nullable column</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#718096">&#8212;</FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">FOREIGN KEY constraint</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#718096">- -</FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">naming convention only, no constraint</FONT></TD></TR></TABLE>>]; __legend [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" COLOR="#a0aec0" BGCOLOR="#ffffff"><TR><TD COLSPAN="2" BGCOLOR="#bee3f8"><FONT COLOR="#1a365d" POINT-SIZE="11"><B>Legend</B></FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#2d3748"><B>PK</B></FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">primary key</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#2d3748"><B>FK</B></FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">foreign key</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#2d3748"><B>?</B></FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">nullable column</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#718096">&#8212;</FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">FOREIGN KEY constraint</FONT></TD></TR><TR><TD ALIGN="LEFT"><FONT COLOR="#718096">- -</FONT></TD><TD ALIGN="LEFT"><FONT COLOR="#2d3748" POINT-SIZE="9">naming convention only, no constraint</FONT></TD></TR></TABLE>>];
google_auth:selected_company:sw -> company:in8:n [color="#c53030", fontcolor="#c53030", tooltip="FK_google_auth_selected_company"]; google_auth:selected_company:w -> company:in8:n [color="#c53030", fontcolor="#c53030", tooltip="FK_google_auth_selected_company"];
category:parent_id:e -> category:id:e [color="#2f855a", fontcolor="#2f855a", tooltip="FK_category_parent"]; category:parent_id:e -> category:id:e [color="#2f855a", fontcolor="#2f855a", tooltip="FK_category_parent"];
company:root_category_id:w -> category:in5:n [color="#2b6cb0", fontcolor="#2b6cb0", tooltip="FK_company_root_category"]; company:root_category_id:w -> category:in5:n [color="#2b6cb0", fontcolor="#2b6cb0", tooltip="FK_company_root_category"];
company:default_language_id:w -> language:in3:n [color="#b7791f", fontcolor="#b7791f", tooltip="FK_company_default_language"]; company:default_language_id:w -> language:in3:n [color="#b7791f", fontcolor="#b7791f", tooltip="FK_company_default_language"];

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 161 KiB

View File

@@ -268,6 +268,7 @@ const MENU = {
items: [ items: [
{ value: 'google_auth.add', label: 'add', hint: 'email → id' }, { value: 'google_auth.add', label: 'add', hint: 'email → id' },
{ value: 'google_auth.getByEmail', label: 'getByEmail', hint: 'email → row' }, { value: 'google_auth.getByEmail', label: 'getByEmail', hint: 'email → row' },
{ value: 'google_auth.getById', label: 'getById', hint: 'googleAuthId → row' },
{ value: 'google_auth.listAllowedEmails', label: 'listAllowedEmails', hint: '→ rows' }, { value: 'google_auth.listAllowedEmails', label: 'listAllowedEmails', hint: '→ rows' },
{ value: 'google_auth.removeAllowedEmail', label: 'removeAllowedEmail', hint: 'email' }, { value: 'google_auth.removeAllowedEmail', label: 'removeAllowedEmail', hint: 'email' },
{ value: 'google_auth.setSelectedCompany', label: 'setSelectedCompany', hint: 'googleAuthId, companyId' }, { value: 'google_auth.setSelectedCompany', label: 'setSelectedCompany', hint: 'googleAuthId, companyId' },
@@ -1106,6 +1107,11 @@ async function runMethod(client, action) {
if (email == null) return; if (email == null) return;
return ['row', await client.google_auth.getByEmail(email)]; return ['row', await client.google_auth.getByEmail(email)];
} }
case 'google_auth.getById': {
const id = await askInt('Auth id');
if (id == null) return;
return ['row', await client.google_auth.getById(Number(id))];
}
case 'google_auth.listAllowedEmails': { case 'google_auth.listAllowedEmails': {
return ['rows', await client.google_auth.listAllowedEmails()]; return ['rows', await client.google_auth.listAllowedEmails()];
} }

View File

@@ -58,6 +58,7 @@ import taxEnsure from './store/tax/ensure.js';
import taxList from './store/tax/list.js'; import taxList from './store/tax/list.js';
import googleAuthAdd from './store/google_auth/add.js'; import googleAuthAdd from './store/google_auth/add.js';
import googleAuthGetByEmail from './store/google_auth/getByEmail.js'; import googleAuthGetByEmail from './store/google_auth/getByEmail.js';
import googleAuthGetById from './store/google_auth/getById.js';
import googleAuthListAllowedEmails from './store/google_auth/listAllowedEmails.js'; import googleAuthListAllowedEmails from './store/google_auth/listAllowedEmails.js';
import googleAuthRemoveAllowedEmail from './store/google_auth/removeAllowedEmail.js'; import googleAuthRemoveAllowedEmail from './store/google_auth/removeAllowedEmail.js';
import googleAuthSetSelectedCompany from './store/google_auth/setSelectedCompany.js'; import googleAuthSetSelectedCompany from './store/google_auth/setSelectedCompany.js';
@@ -162,6 +163,7 @@ export default class LibDashStore {
this.google_auth = { this.google_auth = {
add: googleAuthAdd(pool), add: googleAuthAdd(pool),
getByEmail: googleAuthGetByEmail(pool), getByEmail: googleAuthGetByEmail(pool),
getById: googleAuthGetById(pool),
listAllowedEmails: googleAuthListAllowedEmails(pool), listAllowedEmails: googleAuthListAllowedEmails(pool),
removeAllowedEmail: googleAuthRemoveAllowedEmail(pool), removeAllowedEmail: googleAuthRemoveAllowedEmail(pool),
setSelectedCompany: googleAuthSetSelectedCompany(pool), setSelectedCompany: googleAuthSetSelectedCompany(pool),

View File

@@ -22,6 +22,7 @@ BEGIN
id INT IDENTITY(1,1) PRIMARY KEY, id INT IDENTITY(1,1) PRIMARY KEY,
email NVARCHAR(320) NOT NULL, email NVARCHAR(320) NOT NULL,
picture NVARCHAR(2048) NULL, picture NVARCHAR(2048) NULL,
picturebinary VARBINARY(MAX) NULL,
sub NVARCHAR(255) NULL, sub NVARCHAR(255) NULL,
created_at DATETIME2 NOT NULL CONSTRAINT DF_google_auth_created_at DEFAULT SYSUTCDATETIME(), created_at DATETIME2 NOT NULL CONSTRAINT DF_google_auth_created_at DEFAULT SYSUTCDATETIME(),
selected_company INT NULL, selected_company INT NULL,

View File

@@ -4,18 +4,30 @@ OUTPUT INSERTED.id
VALUES (@email) VALUES (@email)
`; `;
export const GET_BY_EMAIL = ` export const GOOGLE_AUTH_COLUMNS = `
SELECT
id, id,
email, email,
picture, picture,
picturebinary AS pictureBinary,
sub, sub,
created_at AS createdAt, created_at AS createdAt,
selected_company AS selectedCompanyId selected_company AS selectedCompanyId
`;
export const GET_BY_EMAIL = `
SELECT
${GOOGLE_AUTH_COLUMNS}
FROM google_auth FROM google_auth
WHERE email = @email WHERE email = @email
`; `;
export const GET_BY_ID = `
SELECT
${GOOGLE_AUTH_COLUMNS}
FROM google_auth
WHERE id = @googleAuthId
`;
export const LIST_ALLOWED_EMAILS = ` export const LIST_ALLOWED_EMAILS = `
SELECT SELECT
id, id,
@@ -41,6 +53,6 @@ WHERE id = @googleAuthId
export const SET_PROFILE = ` export const SET_PROFILE = `
UPDATE google_auth UPDATE google_auth
SET picture = @picture, sub = @sub SET picture = @picture, picturebinary = @pictureBinary, sub = @sub
WHERE id = @googleAuthId WHERE id = @googleAuthId
`; `;

View File

@@ -0,0 +1,12 @@
import { GET_BY_ID } from '../../sql/google_auth.js';
export default function getById(pool) {
return async (googleAuthId) => {
const result = await pool
.request()
.input('googleAuthId', googleAuthId)
.query(GET_BY_ID);
return result.recordset[0];
};
}

View File

@@ -1,11 +1,19 @@
import { SET_PROFILE } from '../../sql/google_auth.js'; import { SET_PROFILE } from '../../sql/google_auth.js';
export default function setProfile(pool) { export default function setProfile(pool) {
return async (googleAuthId, { picture = null, sub = null } = {}) => { return async (
googleAuthId,
{ picture = null, pictureBinary = null, sub = null } = {},
) => {
if (pictureBinary != null && !Buffer.isBuffer(pictureBinary)) {
throw new Error('Invalid pictureBinary');
}
const result = await pool const result = await pool
.request() .request()
.input('googleAuthId', googleAuthId) .input('googleAuthId', googleAuthId)
.input('picture', picture) .input('picture', picture)
.input('pictureBinary', pictureBinary)
.input('sub', sub) .input('sub', sub)
.query(SET_PROFILE); .query(SET_PROFILE);