diff --git a/apps/ktb-static/script.js b/apps/ktb-static/script.js index ce3db82..d87e536 100644 --- a/apps/ktb-static/script.js +++ b/apps/ktb-static/script.js @@ -1,4 +1,4 @@ -let HOST = 'http://localhost:1225' +let HOST = '' //blank for same-origin document.addEventListener("alpine:init", () => { Alpine.store("dictionary", { diff --git a/apps/ktb-static/styles.css b/apps/ktb-static/styles.css index 7338029..88081dd 100644 --- a/apps/ktb-static/styles.css +++ b/apps/ktb-static/styles.css @@ -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; + } +} diff --git a/apps/vdb-backend/package.json b/apps/vdb-backend/package.json index cbaf2c9..becc2fe 100644 --- a/apps/vdb-backend/package.json +++ b/apps/vdb-backend/package.json @@ -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", diff --git a/apps/vdb-backend/src/config/dbconfig.ts b/apps/vdb-backend/src/config/dbconfig.ts index 43efa42..84007dd 100644 --- a/apps/vdb-backend/src/config/dbconfig.ts +++ b/apps/vdb-backend/src/config/dbconfig.ts @@ -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: [], }) \ No newline at end of file diff --git a/apps/vdb-backend/src/db/dbmodel.ts b/apps/vdb-backend/src/db/dbmodel.ts index 33ec163..1c67c55 100644 --- a/apps/vdb-backend/src/db/dbmodel.ts +++ b/apps/vdb-backend/src/db/dbmodel.ts @@ -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[]; +} \ No newline at end of file diff --git a/apps/vdb-backend/src/index.ts b/apps/vdb-backend/src/index.ts index dbb0a8c..e064c72 100644 --- a/apps/vdb-backend/src/index.ts +++ b/apps/vdb-backend/src/index.ts @@ -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); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7896ae..0843124 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: {}