diff --git a/client/src/app/shared/atoms/textbox.component.ts b/client/src/app/shared/atoms/textbox.component.ts
new file mode 100644
index 0000000..71ab07d
--- /dev/null
+++ b/client/src/app/shared/atoms/textbox.component.ts
@@ -0,0 +1,24 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
+import { FormComponent } from '../formBuilder/types/form.type';
+import { TextBox } from '../formBuilder/types/atoms-types';
+
+@Component({
+ selector: 'textbox',
+ template: `
+
+ `,
+})
+export class TextBoxComponent implements OnInit {
+ @Input() field!: FormComponent;
+ @Input() form!: FormControl;
+ constructor() {}
+
+ ngOnInit() {}
+}
diff --git a/client/src/app/shared/formBuilder/field-builder/field-builder.component.ts b/client/src/app/shared/formBuilder/field-builder/field-builder.component.ts
new file mode 100644
index 0000000..694acf7
--- /dev/null
+++ b/client/src/app/shared/formBuilder/field-builder/field-builder.component.ts
@@ -0,0 +1,51 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FormComponent } from '../types/form.type';
+import { FormGroup, FormControl } from '@angular/forms';
+
+@Component({
+ selector: 'field-builder',
+ template: `
+
+ `,
+})
+export class FieldBuilderComponent implements OnInit {
+ @Input({ required: true }) field!: FormComponent;
+ @Input() form!: FormGroup;
+
+ get isValid() {
+ return this.form.controls[this.field.id].valid;
+ }
+ get isDirty() {
+ return this.form.controls[this.field.id].dirty;
+ }
+
+ get control(): FormControl {
+ return this.form.controls[this.field.id] as FormControl;
+ }
+
+ constructor() {}
+
+ ngOnInit() {}
+}
diff --git a/client/src/app/shared/formBuilder/form-id.ts b/client/src/app/shared/formBuilder/form-id.ts
new file mode 100644
index 0000000..f1b528b
--- /dev/null
+++ b/client/src/app/shared/formBuilder/form-id.ts
@@ -0,0 +1,15 @@
+export class FormID {
+ private static CHARACTERS =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ private static BASE_ID = 'form-';
+
+ private static generateRandomString(length: number): string {
+ let result = '';
+ for (let i = 0; i < length; i++) {
+ result += this.CHARACTERS.charAt(
+ Math.floor(Math.random() * this.CHARACTERS.length)
+ );
+ }
+ return result;
+ }
+}
diff --git a/client/src/app/shared/formBuilder/formBuilder.component.ts b/client/src/app/shared/formBuilder/formBuilder.component.ts
new file mode 100644
index 0000000..1311be0
--- /dev/null
+++ b/client/src/app/shared/formBuilder/formBuilder.component.ts
@@ -0,0 +1,19 @@
+import { Component, Input } from '@angular/core';
+import { FormGroup } from '@angular/forms';
+
+@Component({
+ selector: 'form-builder',
+ template: `
+
+ `,
+})
+export class FormBuilderComponent {
+ @Input() fields: any[] = [];
+ @Input() formGroup!: FormGroup;
+ constructor() {}
+}
diff --git a/client/src/app/shared/formBuilder/formBuilder.module.ts b/client/src/app/shared/formBuilder/formBuilder.module.ts
new file mode 100644
index 0000000..0f63fe6
--- /dev/null
+++ b/client/src/app/shared/formBuilder/formBuilder.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { FormBuilderComponent } from './formBuilder.component';
+import { CommonModule } from '@angular/common';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FieldBuilderComponent } from './field-builder/field-builder.component';
+import { TextBoxComponent } from '../atoms/textbox.component';
+
+@NgModule({
+ imports: [CommonModule, ReactiveFormsModule],
+ declarations: [FormBuilderComponent, FieldBuilderComponent, TextBoxComponent],
+ exports: [FormBuilderComponent],
+ providers: [],
+})
+export class FormBuilderModule {}
diff --git a/client/src/app/shared/formBuilder/types/atoms-types/index.ts b/client/src/app/shared/formBuilder/types/atoms-types/index.ts
new file mode 100644
index 0000000..eb1e3b3
--- /dev/null
+++ b/client/src/app/shared/formBuilder/types/atoms-types/index.ts
@@ -0,0 +1,34 @@
+type TextBox = {
+ type: 'text';
+ body: {
+ type: string;
+ label: string;
+ name: string;
+ value: string;
+ required: boolean;
+ placeholder: string;
+ };
+};
+
+type CheckBox = {
+ type: 'checkbox';
+ body: {
+ label: string;
+ name: string;
+ value: string;
+ required: boolean;
+ placeholder: string;
+ };
+};
+
+type DropDown = {
+ type: 'dropdown';
+ body: {
+ label: string;
+ items: string[];
+ required: boolean;
+ placeholder: string;
+ };
+};
+
+export { TextBox, CheckBox, DropDown };
diff --git a/client/src/app/shared/formBuilder/types/form.type.ts b/client/src/app/shared/formBuilder/types/form.type.ts
new file mode 100644
index 0000000..7d2b1f0
--- /dev/null
+++ b/client/src/app/shared/formBuilder/types/form.type.ts
@@ -0,0 +1,6 @@
+import { CheckBox, DropDown, TextBox } from './atoms-types';
+
+export type FormComponent = (TextBox | CheckBox | DropDown) & {
+ id: string;
+ type: string;
+};
diff --git a/recipe_manager.code-workspace b/recipe_manager.code-workspace
index e329506..a40211a 100644
--- a/recipe_manager.code-workspace
+++ b/recipe_manager.code-workspace
@@ -6,5 +6,16 @@
{
"path": "./server"
}
- ]
+ ],
+ "settings": {
+ "sqltools.connections": [
+ {
+ "previewLimit": 50,
+ "driver": "SQLite",
+ "name": "Taobin Recipe Manager (Server)",
+ "database": "${workspaceFolder:server}/data/database.db"
+ }
+ ],
+ "sqltools.useNodeRuntime": true
+ }
}
\ No newline at end of file
diff --git a/server/cofffeemachineConfig b/server/cofffeemachineConfig
index 47004d3..9fc8791 160000
--- a/server/cofffeemachineConfig
+++ b/server/cofffeemachineConfig
@@ -1 +1 @@
-Subproject commit 47004d38a2c9468fa5cdbadfaf792fb9bd234a1f
+Subproject commit 9fc8791818e56cc8bd9065beed223ec96844f146
diff --git a/server/data/database.db b/server/data/database.db
new file mode 100644
index 0000000..4005aa1
Binary files /dev/null and b/server/data/database.db differ
diff --git a/server/data/migrations/20231127070350_init.down.sql b/server/data/migrations/20231127070350_init.down.sql
new file mode 100644
index 0000000..e69de29
diff --git a/server/data/migrations/20231127070350_init.up.sql b/server/data/migrations/20231127070350_init.up.sql
new file mode 100644
index 0000000..be67617
--- /dev/null
+++ b/server/data/migrations/20231127070350_init.up.sql
@@ -0,0 +1,10 @@
+-- slqlite3
+-- create users table
+CREATE TABLE users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ name TEXT NOT NULL,
+ email TEXT NOT NULL,
+ password TEXT NOT NULL,
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
\ No newline at end of file
diff --git a/server/data/sqlite.go b/server/data/sqlite.go
new file mode 100644
index 0000000..e47f694
--- /dev/null
+++ b/server/data/sqlite.go
@@ -0,0 +1,8 @@
+package data
+
+import "github.com/jmoiron/sqlx"
+
+func NewSqliteDatabase() *sqlx.DB {
+ db := sqlx.MustConnect("sqlite3", "./data/database.db")
+ return db
+}
diff --git a/server/go.mod b/server/go.mod
index 37aff73..a92e933 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -24,7 +24,11 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)
-require github.com/gorilla/websocket v1.5.0 // indirect
+require (
+ github.com/gorilla/websocket v1.5.0 // indirect
+ github.com/jmoiron/sqlx v1.3.5 // indirect
+ github.com/mattn/go-sqlite3 v1.14.18 // indirect
+)
require (
github.com/fsnotify/fsnotify v1.6.0 // indirect
diff --git a/server/go.sum b/server/go.sum
index aa64271..106d85d 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -71,6 +71,7 @@ github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vz
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -150,6 +151,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
+github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -161,8 +164,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
+github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
diff --git a/server/routers/auth.go b/server/routers/auth.go
index e9fce2c..f4ca6e8 100644
--- a/server/routers/auth.go
+++ b/server/routers/auth.go
@@ -10,6 +10,7 @@ import (
"recipe-manager/services/oauth"
"github.com/go-chi/chi/v5"
+ "go.uber.org/zap"
"golang.org/x/oauth2"
)
@@ -87,6 +88,8 @@ func (ar *AuthRouter) Route(r chi.Router) {
value.Add("redirect_to", redirect_to)
}
+ Log.Info("User Log-In Success", zap.String("user", user.Name), zap.String("email", user.Email))
+
// redirect to frontend with token and refresh token
w.Header().Add("set-cookie", "access_token="+token.AccessToken+"; Path=/; HttpOnly; SameSite=None; Secure; Max-Age=3600")
w.Header().Add("set-cookie", "refresh_token="+token.RefreshToken+"; Path=/; HttpOnly; SameSite=None; Secure")
diff --git a/server/routers/recipe.go b/server/routers/recipe.go
index 9ffca89..c3b94f9 100644
--- a/server/routers/recipe.go
+++ b/server/routers/recipe.go
@@ -303,7 +303,7 @@ func (rr *RecipeRouter) Route(r chi.Router) {
// check if changed
// Log.Debug("Check if changed", zap.Any("result", rr.data.GetRecipe01ByProductCode(changes.ProductCode)))
- file, _ := os.Create(path.Join("./cofffeemachineConfig", countryID[:3], filename))
+ file, _ := os.Create(path.Join("./cofffeemachineConfig", countryID, filename))
if err != nil {
Log.Error("Error when tried to create file", zap.Error(err))
return
@@ -311,7 +311,7 @@ func (rr *RecipeRouter) Route(r chi.Router) {
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
- err = encoder.Encode(rr.data.GetCurrentRecipe())
+ err = encoder.Encode(rr.data.GetRecipe(countryID, filename))
if err != nil {
Log.Error("Error when write file", zap.Error(err))
diff --git a/server/server.go b/server/server.go
index d84e989..5717722 100644
--- a/server/server.go
+++ b/server/server.go
@@ -154,11 +154,6 @@ func (s *Server) createHandler() {
}
ctx := context.WithValue(r.Context(), "user", user)
- if user == nil {
- Log.Error("User is not authenticated or timed out", zap.Any("requester", user))
- } else {
- Log.Info("User is authenticated", zap.String("user", user.Name))
- }
next.ServeHTTP(w, r.WithContext(ctx))
})