(wip: auth)
Some checks are pending
SSH Deploy to staging / remote-update (push) Waiting to run

This commit is contained in:
Sheldon Cooper 2026-08-13 22:23:29 -04:00
parent da7d75641d
commit 81f86acc7b
7 changed files with 148 additions and 18 deletions

View file

@ -1,4 +1,4 @@
let HOST = 'http://localhost:1225'
let HOST = '' //blank for same-origin
document.addEventListener("alpine:init", () => {
Alpine.store("dictionary", {

View file

@ -26,18 +26,6 @@
--shadow-lg: 0 4px 8px var(--color-shadow);
}
/* Small devices (portrait tablets and large phones, 600px and up) */
@media (max-width: 600px) {
.cards-container {
grid-template-columns: 1fr;
background-color: #fb0;
}
.is-expanded {
grid-row: span 1;
grid-column: span 1;
}
}
* {
margin: 0;
@ -384,3 +372,19 @@ button:disabled{
color: var(--color-text-secondary);
font-size: 0.95rem;
}
/* Small devices (portrait tablets and large phones, 600px and up) */
@media (max-width: 600px) {
.cards-container {
display: flex;
flex-direction: column;
}
.card {
width: 100%;
}
.sidebar {
display: none;
}
}

View file

@ -28,6 +28,7 @@
"google-auth-library": "^9.15.1",
"googleapis": "^149.0.0",
"node-fetch": "^3.3.2",
"passport": "^0.7.0",
"reflect-metadata": "^0.2.2",
"sqlite3": "^5.1.7",
"typeorm": "0.3.26",

View file

@ -1,6 +1,6 @@
import "reflect-metadata"
import { DataSource } from "typeorm"
import {Lemma, WordForm, Media, Example, Definition, Comment, PartOfSpeech, Lect} from "../db/dbmodel.js"
import {Lemma, WordForm, Media, Example, Definition, Comment, PartOfSpeech, Lect, Role, User} from "../db/dbmodel.js"
const persistent_path = process.env.VI_DB_PERSISTENT_PATH || "./res";
@ -9,7 +9,7 @@ export const appDataSource = new DataSource({
database:`${persistent_path}/dev.sqlite`,
synchronize: true,
logging: false,
entities: [Lemma, WordForm, Example, Media, Definition, Comment, PartOfSpeech, Lect],
entities: [Lemma, WordForm, Example, Media, Definition, Comment, PartOfSpeech, Lect, Role, User],
migrations: [],
subscribers: [],
})

View file

@ -116,3 +116,24 @@ export class PartOfSpeech extends BaseEntity {
@Column({ nullable: false, unique: true, type: "text" })
short_form: string;
}
@Entity()
export class User extends BaseEntity {
@PrimaryColumn({ type: "text", nullable: false })
user_name: string;
@ManyToMany(() => Role, (role) => role.users, {eager:true})
@JoinTable()
roles: Role[];
}
@Entity()
export class Role extends BaseEntity {
@PrimaryColumn({ type: "text", nullable: false})
role_name: string;
@ManyToMany(() => User, (user) => user.roles, {eager:true})
@JoinTable()
users: User[];
}

View file

@ -1,16 +1,19 @@
import "reflect-metadata";
import { SAMPLE } from "@repo/common/sample";
import path from "path"
import { fileURLToPath } from 'url';
import express from "express";
import crypto from "crypto";
import fs from "fs";
import { appDataSource } from "./config/dbconfig.js";
import { Lemma, WordForm, Lect, Definition, Example } from "./db/dbmodel.js";
import { Lemma, WordForm, Lect, Definition, Example, Role, User } from "./db/dbmodel.js";
import "@total-typescript/ts-reset";
import { Like, In } from "typeorm";
import cors from "cors";
const RELOAD_SHEET_ON_START = false;
const SOURCE_FILE = "res/sample.tsv";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
appDataSource
.initialize()
@ -19,15 +22,86 @@ appDataSource
if (RELOAD_SHEET_ON_START) {
await loadSheet();
await ensureDefaultRoles();
}
})
.catch((error) => console.log(error));
async function ensureDefaultRoles(){
const ADMIN_NAME = "admin"
const EDITOR_NAME = "editor"
const READER_NAME = "reader"
let admin_role = new Role();
admin_role.role_name = ADMIN_NAME;
let editor_role = new Role();
editor_role.role_name = EDITOR_NAME;
let reader_role = new Role();
reader_role.role_name = READER_NAME;
Role.save([admin_role, editor_role, reader_role]);
let admin_user: User;
let editor_user: User;
let reader_user: User;
await User.findOne({where:
{user_name:ADMIN_NAME}
}).then(async (u)=>{
//if no admin user is detected, generate one with the admin role
if (!u){
admin_user = new User();
admin_user.user_name = ADMIN_NAME;
admin_user.roles = [admin_role];
await admin_user
.save()
.then((u)=>{
admin_user = u;
});
}
})
await User.findOne({where:
{user_name:EDITOR_NAME}
}).then(async (u)=>{
if (!u){
editor_user = new User();
editor_user.user_name = EDITOR_NAME;
editor_user.roles = [editor_role];
await editor_user
.save()
.then((u)=>{
editor_user = u;
});
}
})
await User.findOne({where:
{user_name:READER_NAME}
}).then(async (u)=>{
if (!u){
reader_user = new User();
reader_user.user_name = READER_NAME;
reader_user.roles = [reader_role];
await reader_user
.save()
.then((u)=>{
reader_user = u;
});
}
})
}
function initExpress() {
const app = express();
const PORT = 1225;
app.use(cors(), express.json());
console.info(path.join(__dirname, '../../ktb-static'));
app.use(express.json(), express.static(path.join(__dirname, '../../ktb-static')));
app.get("/sample", (_req, res) => {
res.status(200).send(SAMPLE);

32
pnpm-lock.yaml generated
View file

@ -41,6 +41,9 @@ importers:
node-fetch:
specifier: ^3.3.2
version: 3.3.2
passport:
specifier: ^0.7.0
version: 0.7.0
reflect-metadata:
specifier: ^0.2.2
version: 0.2.2
@ -1659,7 +1662,7 @@ packages:
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
deprecated: Glob versions prior to v9 are no longer supported
globals@14.0.0:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
@ -2210,6 +2213,14 @@ packages:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
passport-strategy@1.0.0:
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
engines: {node: '>= 0.4.0'}
passport@0.7.0:
resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==}
engines: {node: '>= 0.4.0'}
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@ -2240,6 +2251,9 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
pause@0.0.1:
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@ -2760,6 +2774,10 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
utils-merge@1.0.1:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
uuid@11.1.0:
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
hasBin: true
@ -4954,6 +4972,14 @@ snapshots:
parseurl@1.3.3: {}
passport-strategy@1.0.0: {}
passport@0.7.0:
dependencies:
passport-strategy: 1.0.0
pause: 0.0.1
utils-merge: 1.0.1
path-browserify@1.0.1: {}
path-exists@4.0.0: {}
@ -4977,6 +5003,8 @@ snapshots:
pathe@2.0.3: {}
pause@0.0.1: {}
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@ -5549,6 +5577,8 @@ snapshots:
util-deprecate@1.0.2: {}
utils-merge@1.0.1: {}
uuid@11.1.0: {}
uuid@9.0.1: {}