Browse Source

clone basic database structure from zhquiz

main
parent
commit
11d44ccfd3
18 changed files with 1594 additions and 0 deletions
  1. +5
    -0
      .dockerignore
  2. +38
    -0
      Dockerfile
  3. +2
    -0
      cache/.gitignore
  4. +22
    -0
      docker-compose.yml
  5. +4
    -0
      initdb.d/00-extension.sql
  6. +3
    -0
      initdb.d/01-function.sql
  7. +22
    -0
      initdb.d/20-user.sql
  8. +44
    -0
      initdb.d/21-quiz.sql
  9. +48
    -0
      initdb.d/23-library.sql
  10. +18
    -0
      initdb.d/24-preset.sql
  11. +26
    -0
      initdb.d/26-radical.sql
  12. +12
    -0
      initdb.d/50-session.sql
  13. +11
    -0
      initdb.d/99-init.sh
  14. +34
    -0
      package.json
  15. +0
    -0
      scripts/.gitkeep
  16. +0
    -0
      src/init.ts
  17. +100
    -0
      tsconfig.json
  18. +1205
    -0
      yarn.lock

+ 5
- 0
.dockerignore View File

@ -0,0 +1,5 @@
**
!/src/
!package.json
!yarn.lock

+ 38
- 0
Dockerfile View File

@ -0,0 +1,38 @@
# Based on debian-slim
FROM postgres:12
RUN mkdir -p /app
RUN apt-get update
# pgroonga extension
# Does not officially support alpine
RUN apt-get install -y curl
RUN curl -O https://packages.groonga.org/debian/groonga-apt-source-latest-buster.deb
RUN apt-get install -y ./groonga-apt-source-latest-buster.deb
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN apt-get update
RUN apt-get install -y postgresql-12-pgdg-pgroonga
RUN apt-get install -y groonga-tokenizer-mecab
RUN apt-get install -y groonga-token-filter-stem
# postgres-json-schema extension
WORKDIR /app
RUN apt-get install -y git make
RUN git clone --depth 1 https://github.com/gavinwahl/postgres-json-schema.git
RUN cd postgres-json-schema && make install
# nodejs14
# Install nodejs 14
WORKDIR /app
RUN apt-get install -y dirmngr apt-transport-https lsb-release ca-certificates jq
RUN curl -sSL https://deb.nodesource.com/setup_14.x | bash -
RUN apt-get install -y nodejs gcc g++ make
RUN npm i -g yarn
COPY package.json yarn.lock ./
RUN yarn --frozen-lockfile
# COPY . .
# RUN yarn build
# RUN jq 'del(.devDependencies)' package.json > tmp.json && mv tmp.json package.json
# RUN yarn --frozen-lockfile

+ 2
- 0
cache/.gitignore View File

@ -0,0 +1,2 @@
*
!.gitignore

+ 22
- 0
docker-compose.yml View File

@ -0,0 +1,22 @@
version: '3'
services:
db:
restart: always
build: .
environment:
POSTGRES_USER: &pguser postgres
POSTGRES_PASSWORD: &pgpass postgres-passwd
POSTGRES_DB: &pgdb jaquiz
TZ: &tz Asia/Bangkok
PGTZ: *tz
ports:
- 5433:5432 # pgAdmin connection port
volumes:
- ./pgdata:/var/lib/postgresql/data
- ./initdb.d:/docker-entrypoint-initdb.d
- ./cache:/app/cache
- ./assets:/app/assets
- ./src:/app/src

+ 4
- 0
initdb.d/00-extension.sql View File

@ -0,0 +1,4 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS pgroonga;
CREATE EXTENSION IF NOT EXISTS "postgres-json-schema";

+ 3
- 0
initdb.d/01-function.sql View File

@ -0,0 +1,3 @@
CREATE FUNCTION array_distinct(anyarray) RETURNS anyarray AS $f$
SELECT array_agg(DISTINCT x) FROM unnest($1) t(x);
$f$ LANGUAGE SQL IMMUTABLE;

+ 22
- 0
initdb.d/20-user.sql View File

@ -0,0 +1,22 @@
CREATE TABLE "user" (
"id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(),
"createdAt" TIMESTAMPTZ DEFAULT now(),
"updatedAt" TIMESTAMPTZ DEFAULT now(),
"identifier" TEXT UNIQUE NOT NULL,
"wk_hash" TEXT NOT NULL,
"level.min" INT DEFAULT 1,
"level.max" INT DEFAULT 3,
"level.vocabulary.showing" TEXT[],
"quiz.settings" JSONB
);
CREATE TRIGGER "t_user_updatedAt"
BEFORE UPDATE ON "user"
FOR EACH ROW
EXECUTE PROCEDURE "f_updatedAt"();
CREATE INDEX "idx_user_updatedAt" ON "user" ("updatedAt");
CREATE INDEX "idx_user_identifier" ON "user" ("identifier");
CREATE INDEX "idx_user_wk_hash" ON "user" ("wk_hash");
INSERT INTO "user" ("id", "identifier") VALUES (uuid_nil(), '');

+ 44
- 0
initdb.d/21-quiz.sql View File

@ -0,0 +1,44 @@
CREATE TABLE "quiz" (
"id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(),
"createdAt" TIMESTAMPTZ DEFAULT now(),
"updatedAt" TIMESTAMPTZ DEFAULT now(),
"userId" UUID NOT NULL REFERENCES "user"("id") ON DELETE CASCADE,
"entry" TEXT NOT NULL,
"type" TEXT NOT NULL,
"direction" TEXT NOT NULL,
"front" TEXT NOT NULL DEFAULT '',
"back" TEXT NOT NULL DEFAULT '',
"hint" TEXT NOT NULL DEFAULT '',
"mnemonic" TEXT NOT NULL DEFAULT '',
"srsLevel" INT,
"nextReview" TIMESTAMPTZ,
"lastRight" TIMESTAMPTZ,
"lastWrong" TIMESTAMPTZ,
"maxRight" INT,
"maxWrong" INT,
"rightStreak" INT,
"wrongStreak" INT
);
CREATE TRIGGER "t_quiz_updatedAt"
BEFORE UPDATE ON "quiz"
FOR EACH ROW
EXECUTE PROCEDURE "f_updatedAt"();
CREATE UNIQUE INDEX "idx_u_quiz" ON "quiz" ("userId", "entry", "type", "direction");
CREATE INDEX "idx_quiz_q" ON "quiz"
USING pgroonga ("front", "back", "hint", "mnemonic")
WITH (plugins='token_filters/stem', token_filters='TokenFilterStem');
CREATE INDEX "idx_quiz_entry" ON "quiz" ("entry");
CREATE INDEX "idx_quiz_updatedAt" ON "quiz" ("updatedAt");
CREATE INDEX "idx_quiz_userId" ON "quiz" ("userId");
CREATE INDEX "idx_quiz_srsLevel" ON "quiz" ("srsLevel");
CREATE INDEX "idx_quiz_nextReview" ON "quiz" ("nextReview");
CREATE INDEX "idx_quiz_lastRight" ON "quiz" ("lastRight");
CREATE INDEX "idx_quiz_lastWrong" ON "quiz" ("lastWrong");
CREATE INDEX "idx_quiz_maxRight" ON "quiz" ("maxRight");
CREATE INDEX "idx_quiz_maxWrong" ON "quiz" ("maxWrong");
CREATE INDEX "idx_quiz_rightStreak" ON "quiz" ("rightStreak");
CREATE INDEX "idx_quiz_wrongStreak" ON "quiz" ("wrongStreak");

+ 48
- 0
initdb.d/23-library.sql View File

@ -0,0 +1,48 @@
CREATE OR REPLACE FUNCTION unwrap_entries (JSONB) RETURNS TEXT[] AS
$func$
DECLARE
it JSONB;
"out" TEXT[] := '{}'::text[];
BEGIN
FOR it IN (SELECT * FROM jsonb_array_elements($1))
LOOP
"out" := "out"||(it ->> 'entry');
END LOOP;
RETURN "out";
END;
$func$ LANGUAGE plpgsql IMMUTABLE;
CREATE TABLE "library" (
"id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(),
"createdAt" TIMESTAMPTZ DEFAULT now(),
"updatedAt" TIMESTAMPTZ DEFAULT now(),
"userId" UUID NOT NULL DEFAULT uuid_nil() REFERENCES "user"("id") ON DELETE CASCADE,
"isShared" BOOLEAN,
"type" TEXT NOT NULL,
"entries" JSONB NOT NULL CHECK ("entries" -> 0 -> 'entry' IS NOT NULL),
"entry" TEXT[] GENERATED ALWAYS AS (unwrap_entries("entries")) STORED,
"title" TEXT NOT NULL,
"description" TEXT NOT NULL DEFAULT '',
"tag" TEXT[] NOT NULL DEFAULT '{}'::TEXT[]
);
CREATE TRIGGER "t_library_updatedAt"
BEFORE UPDATE ON "library"
FOR EACH ROW
EXECUTE PROCEDURE "f_updatedAt"();
CREATE INDEX "idx_library_updatedAt" ON "library" ("updatedAt");
CREATE INDEX "idx_library_userId" ON "library" ("userId");
CREATE INDEX "idx_library_isShared" ON "library" ("isShared");
CREATE INDEX "idx_library_type" ON "library" ("type");
CREATE INDEX "idx_library_entry" ON "library"
USING pgroonga("entry");
CREATE INDEX "idx_library_title_description" ON "library"
USING pgroonga(
"title",
"description"
)
WITH (plugins='token_filters/stem', token_filters='TokenFilterStem');
CREATE INDEX "idx_library_tag" ON "library" USING pgroonga ("tag");

+ 18
- 0
initdb.d/24-preset.sql View File

@ -0,0 +1,18 @@
CREATE TABLE "quiz_preset" (
"id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(),
"createdAt" TIMESTAMPTZ DEFAULT now(),
"updatedAt" TIMESTAMPTZ DEFAULT now(),
"userId" UUID NOT NULL REFERENCES "user"("id") ON DELETE CASCADE,
"name" TEXT NOT NULL,
"settings" JSONB NOT NULL
);
CREATE TRIGGER "t_quiz_preset_updatedAt"
BEFORE UPDATE ON "quiz_preset"
FOR EACH ROW
EXECUTE PROCEDURE "f_updatedAt"();
CREATE INDEX "idx_quiz_preset_updatedAt" ON "quiz_preset" ("updatedAt");
CREATE INDEX "idx_quiz_preset_userId" ON "quiz_preset" ("userId");
CREATE INDEX "idx_quiz_preset_name" ON "quiz_preset"
USING pgroonga ("name");

+ 26
- 0
initdb.d/26-radical.sql View File

@ -0,0 +1,26 @@
CREATE TABLE radical (
"entry" TEXT NOT NULL PRIMARY KEY,
"sub" TEXT[] NOT NULL,
"sup" TEXT[] NOT NULL,
"var" TEXT[] NOT NULL
);
CREATE INDEX idx_radical_search ON radical
USING pgroonga ("entry", "sub", "sup", "var")
WITH (tokenizer='TokenUnigram');
CREATE INDEX idx_radical_synonyms ON radical
USING pgroonga ("entry" pgroonga_text_term_search_ops_v2)
WITH (tokenizer='TokenUnigram');
CREATE OR REPLACE FUNCTION character_expand (TEXT) RETURNS TEXT AS
$func$
DECLARE
exp TEXT := pgroonga_query_expand('radical',
'entry',
'var',
$1);
BEGIN
RETURN $1||' OR '||exp;
END;
$func$ LANGUAGE plpgsql IMMUTABLE;

+ 12
- 0
initdb.d/50-session.sql View File

@ -0,0 +1,12 @@
-- cat ./node_modules/connect-pg-simple/table.sql
CREATE TABLE "session" (
"sid" varchar NOT NULL COLLATE "default",
"sess" json NOT NULL,
"expire" timestamp(6) NOT NULL
)
WITH (OIDS=FALSE);
ALTER TABLE "session" ADD CONSTRAINT "session_pkey" PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE;
CREATE INDEX "IDX_session_expire" ON "session" ("expire");

+ 11
- 0
initdb.d/99-init.sh View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
pg_ctl -o "-c listen_addresses='localhost'" -w restart
cd /app
if [[ -d "./lib" ]]; then
node ./lib/init.js
else
yarn ts ./src/init.ts
fi

+ 34
- 0
package.json View File

@ -0,0 +1,34 @@
{
"name": "@zhquiz/database",
"version": "1.0.0",
"private": true,
"main": "index.js",
"repository": "git@git.polv.cc:jaquiz/database.git",
"author": "Pacharapol Withayasakpunt <polv@polv.cc>",
"license": "MIT",
"scripts": {
"ts": "ts-node -O '{\"noImplicitAny\":false}'"
},
"dependencies": {
"@databases/pg": "^5.1.1",
"axios": "^0.22.0",
"better-sqlite3": "^7.4.3",
"fast-glob": "^3.2.7",
"js-yaml": "^4.1.0",
"jsonschema-definer": "^1.3.2"
},
"devDependencies": {
"@types/better-sqlite3": "^7.4.0",
"@types/js-yaml": "^4.0.3",
"@types/node": "^16.9.6",
"import-sort-parser-typescript": "^6.0.0",
"ts-node": "^10.2.1",
"typescript": "^4.4.3"
},
"importSort": {
".js, .ts": {
"parser": "typescript",
"style": "module"
}
}
}

+ 0
- 0
scripts/.gitkeep View File


+ 0
- 0
src/init.ts View File


+ 100
- 0
tsconfig.json View File

@ -0,0 +1,100 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
// "incremental": true, /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2019", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
"strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
"strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
"strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
"strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
"noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
"useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
"alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
"noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
"noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
"exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
"noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
"noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
"noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
"noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
"noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

+ 1205
- 0
yarn.lock
File diff suppressed because it is too large
View File


Loading…
Cancel
Save