Compare commits
3 Commits
d77f0f8359
...
03036fdffd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03036fdffd | ||
|
|
5bc7a48f75 | ||
|
|
719edfd33a |
82
db/migration/base/001.up.base.sql
Normal file
82
db/migration/base/001.up.base.sql
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
create domain dmn_code as text not null;
|
||||||
|
create domain dmn_money as numeric(22, 4);
|
||||||
|
create domain dmn_qty numeric(18, 6);
|
||||||
|
create domain dmn_rate as numeric(5, 2);
|
||||||
|
|
||||||
|
create function iif(condition boolean, true_result anyelement, false_result anyelement) returns anyelement
|
||||||
|
immutable
|
||||||
|
language sql
|
||||||
|
as
|
||||||
|
$$
|
||||||
|
SELECT CASE
|
||||||
|
WHEN condition THEN true_result
|
||||||
|
ELSE false_result
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE
|
||||||
|
OR REPLACE FUNCTION zllog()
|
||||||
|
RETURNS trigger
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS
|
||||||
|
$BODY$
|
||||||
|
BEGIN
|
||||||
|
if
|
||||||
|
(TG_OP = 'INSERT') then
|
||||||
|
if (new.zlins_dttm is null) then
|
||||||
|
new.zlins_dttm = current_timestamp;
|
||||||
|
END if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if
|
||||||
|
(TG_OP = 'UPDATE') then
|
||||||
|
if (new.zlupd_dttm is null) then
|
||||||
|
new.zlupd_dttm = current_timestamp;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$BODY$;
|
||||||
|
|
||||||
|
|
||||||
|
create table logdml
|
||||||
|
(
|
||||||
|
id bigint generated by default as identity
|
||||||
|
primary key,
|
||||||
|
opr text,
|
||||||
|
dttm timestamp with time zone default CURRENT_TIMESTAMP not null,
|
||||||
|
src_table text,
|
||||||
|
src_id bigint,
|
||||||
|
usr_id bigint,
|
||||||
|
msg text,
|
||||||
|
old_val jsonb,
|
||||||
|
new_val jsonb
|
||||||
|
);
|
||||||
|
|
||||||
|
create index ndx_logdml_dttm
|
||||||
|
on logdml (dttm);
|
||||||
|
|
||||||
|
create index ndx_logdml_dttm_desc
|
||||||
|
on logdml (dttm desc);
|
||||||
|
|
||||||
|
create index ndx_logdml_table
|
||||||
|
on logdml (src_table, src_id);
|
||||||
|
|
||||||
|
|
||||||
|
create table sykv
|
||||||
|
(
|
||||||
|
key text not null
|
||||||
|
primary key,
|
||||||
|
val text
|
||||||
|
);
|
||||||
|
|
||||||
|
create table symigrate
|
||||||
|
(
|
||||||
|
id bigint generated by default as identity
|
||||||
|
primary key,
|
||||||
|
tracking_name text,
|
||||||
|
dttm timestamp default CURRENT_TIMESTAMP,
|
||||||
|
last_script integer,
|
||||||
|
log text
|
||||||
|
);
|
||||||
24
db/migration/migrate.sh
Executable file
24
db/migration/migrate.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
read -p "Server [localhost] : " SERVER
|
||||||
|
SERVER=${SERVER:-localhost}
|
||||||
|
|
||||||
|
read -p "Veritabanı : $1" DB
|
||||||
|
DB=${DB:-$1}
|
||||||
|
|
||||||
|
read -p "Yeniden oluşturulsun mu? (E/H) [H] : " CREATE
|
||||||
|
CREATE=${CREATE:-H}
|
||||||
|
|
||||||
|
CREATESTR=""
|
||||||
|
DROPSTR=""
|
||||||
|
if [[ ${CREATE} =~ (E|e) ]]; then
|
||||||
|
CREATESTR="-create"
|
||||||
|
read -p "Eski mevcutsa silinsin mi? (E/H) [E] : " DROP
|
||||||
|
DROP=${DROP:-E}
|
||||||
|
if [[ ${DROP} =~ (E|e) ]]; then
|
||||||
|
DROPSTR="-drop-if-exists"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
migrater -folder ./base -db ${DB} -host ${SERVER} ${CREATESTR} ${DROPSTR} -password tayitkan -tracker base -migrate-table symigrate
|
||||||
|
# migrater -folder ./seed -db ${DB} -host ${SERVER} ${CREATESTR} ${DROPSTR} -password tayitkan -tracker seed -migrate-table symigrate
|
||||||
23
svc/.gitignore
vendored
Normal file
23
svc/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
### Go template
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
18
svc/api/public/login.go
Normal file
18
svc/api/public/login.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package public
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.makki.io/makki/libgo/mhttp"
|
||||||
|
"git.makki.io/makki/libgo/svc"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func login(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authResp, err := svc.S.Authenticate(r)
|
||||||
|
if err != nil {
|
||||||
|
mhttp.InternalServerError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientResp := authResp.GetEndUserReponse()
|
||||||
|
mhttp.ResponseSuccess(w, clientResp)
|
||||||
|
}
|
||||||
14
svc/api/public/zrouter.go
Normal file
14
svc/api/public/zrouter.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package public
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router() http.Handler {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
// user authentication
|
||||||
|
r.Post("/login", login)
|
||||||
|
return r
|
||||||
|
}
|
||||||
108
svc/api/zhandler.go
Normal file
108
svc/api/zhandler.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bordro-esleme/api/public"
|
||||||
|
"fmt"
|
||||||
|
"git.makki.io/makki/libgo/nauth"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.makki.io/makki/libgo/enums"
|
||||||
|
"git.makki.io/makki/libgo/svc"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
"github.com/go-chi/cors"
|
||||||
|
"github.com/go-chi/jwtauth/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HttpHandler(re enums.TRunEnv) http.Handler {
|
||||||
|
mux := chi.NewRouter()
|
||||||
|
|
||||||
|
// Gerekli middleware stack
|
||||||
|
mux.Use(middleware.RequestID)
|
||||||
|
mux.Use(middleware.RealIP)
|
||||||
|
mux.Use(middleware.Logger)
|
||||||
|
|
||||||
|
// Eğer trace middleware'ı kullanırsak buna gerek kalmayacak sanırım kontrol etmeli
|
||||||
|
// https://github.com/go-chi/httptracer
|
||||||
|
mux.Use(middleware.Recoverer)
|
||||||
|
acors := cors.New(cors.Options{
|
||||||
|
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
|
||||||
|
AllowedOrigins: []string{"*"},
|
||||||
|
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
|
||||||
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||||
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||||
|
ExposedHeaders: []string{"Link"},
|
||||||
|
AllowCredentials: true,
|
||||||
|
MaxAge: 300, // Maximum value not ignored by any of major browsers
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.Use(acors.Handler)
|
||||||
|
|
||||||
|
// Set a timeout value on the request context (ctx), that will signal
|
||||||
|
// through ctx.Done() that the request has timed out and further
|
||||||
|
// processing should be stopped.
|
||||||
|
// todo: bu belki endpoint bazında özelleştirlebilinir...
|
||||||
|
// mux.Use(middleware.Timeout(2500 * time.Millisecond))
|
||||||
|
|
||||||
|
mux.Route("/api", func(mr chi.Router) {
|
||||||
|
// Public Route endpoints
|
||||||
|
mr.Mount("/", public.Router())
|
||||||
|
//mr.Mount("/admin", admin.Router())
|
||||||
|
|
||||||
|
//protected end points
|
||||||
|
mr.Group(func(r chi.Router) {
|
||||||
|
// Seek, verify and validate JWT tokens
|
||||||
|
r.Use(jwtauth.Verifier(nauth.JWT))
|
||||||
|
|
||||||
|
// Handle valid / invalid tokens.
|
||||||
|
r.Use(nauth.CheckTokenValidity)
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handle Websocket
|
||||||
|
// mux.HandleFunc("/ws", ws.Handle)
|
||||||
|
|
||||||
|
// SPA Routes
|
||||||
|
mux.Group(func(r chi.Router) {
|
||||||
|
r.NotFound(index)
|
||||||
|
r.Get("/", index)
|
||||||
|
})
|
||||||
|
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func index(w http.ResponseWriter, r *http.Request) {
|
||||||
|
p := r.URL.Path
|
||||||
|
if !strings.HasPrefix(p, "/") {
|
||||||
|
p = "/" + p
|
||||||
|
r.URL.Path = p
|
||||||
|
}
|
||||||
|
p = path.Clean(p)
|
||||||
|
|
||||||
|
if strings.HasPrefix(p, "/api") {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uiPath := "./ui"
|
||||||
|
if svc.S.RunEnv() == enums.RunEnvLocal {
|
||||||
|
uiPath = "../../ui/dist/spa"
|
||||||
|
}
|
||||||
|
|
||||||
|
name := path.Join(uiPath, filepath.FromSlash(p))
|
||||||
|
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
http.ServeFile(w, r, fmt.Sprintf("%s/index.html", uiPath))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
http.ServeFile(w, r, name)
|
||||||
|
}
|
||||||
13
svc/go.mod
Normal file
13
svc/go.mod
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module bordro-esleme
|
||||||
|
|
||||||
|
go 1.22.1
|
||||||
|
|
||||||
|
toolchain go1.22.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.makki.io/makki/libgo v0.0.0-20240408174556-52dd3c28a9b9 // indirect
|
||||||
|
git.notitek.com.tr/common/notgo v0.0.0-20240408194744-d12ce3096580 // indirect
|
||||||
|
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
||||||
|
github.com/go-chi/cors v1.2.1 // indirect
|
||||||
|
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||||
|
)
|
||||||
45
svc/main.go
Normal file
45
svc/main.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bordro-esleme/api"
|
||||||
|
"git.makki.io/makki/libgo/dbu"
|
||||||
|
"git.makki.io/makki/libgo/mlog"
|
||||||
|
"git.makki.io/makki/libgo/svc"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// init base service
|
||||||
|
s := svc.Init("bordro-esleme", "bordroesleme", 40300, 40301)
|
||||||
|
|
||||||
|
// init logger
|
||||||
|
mlog.InitLogger(s.DevMode())
|
||||||
|
|
||||||
|
// connnect to db
|
||||||
|
dbConnectionString := s.DBConnectionString()
|
||||||
|
err := dbu.Connect(s.Ctx, dbConnectionString, s.SqlsDir(), s.DevMode())
|
||||||
|
if err != nil {
|
||||||
|
mlog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//kv table adını atayalım
|
||||||
|
dbu.DB.SetTableNameKV("sykv")
|
||||||
|
|
||||||
|
err = dbu.DB.Ping(s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
mlog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//subscribe to auth server
|
||||||
|
expire := time.Hour * 1
|
||||||
|
if svc.S.DevMode() {
|
||||||
|
expire = time.Hour * 48
|
||||||
|
}
|
||||||
|
err = s.SubscribeToAuthServer(expire)
|
||||||
|
if err != nil {
|
||||||
|
mlog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init appplication service
|
||||||
|
s.StartHttp(api.HttpHandler(s.RunEnv()))
|
||||||
|
}
|
||||||
@@ -67,7 +67,7 @@ module.exports = configure(function (ctx) {
|
|||||||
return process.env.CUSTOM_API
|
return process.env.CUSTOM_API
|
||||||
} else {
|
} else {
|
||||||
if (ctx.dev) {
|
if (ctx.dev) {
|
||||||
return 'http://localhost:41000/api'
|
return 'http://localhost:40300/api'
|
||||||
} else {
|
} else {
|
||||||
return '/api'
|
return '/api'
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ module.exports = configure(function (ctx) {
|
|||||||
return process.env.CUSTOM_WS
|
return process.env.CUSTOM_WS
|
||||||
} else {
|
} else {
|
||||||
if (ctx.dev) {
|
if (ctx.dev) {
|
||||||
return 'ws://127.0.0.1:41000/ws'
|
return 'ws://127.0.0.1:40300/ws'
|
||||||
} else {
|
} else {
|
||||||
return 'wss://bres.notitek.com.tr/ws'
|
return 'wss://bres.notitek.com.tr/ws'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import axios from 'axios'
|
|||||||
// good idea to move this instance creation inside of the
|
// good idea to move this instance creation inside of the
|
||||||
// "export default () => {}" function below (which runs individually
|
// "export default () => {}" function below (which runs individually
|
||||||
// for each client)
|
// for each client)
|
||||||
const api = axios.create({ baseURL: 'https://api.example.com' })
|
const api = axios.create({ baseURL: process.env.apiAddr })
|
||||||
|
|
||||||
export default boot(({ app }) => {
|
export default boot(({ app }) => {
|
||||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||||
|
|||||||
@@ -7,22 +7,6 @@
|
|||||||
<q-img src="logo.png" width="120px" fit="contain"/>
|
<q-img src="logo.png" width="120px" fit="contain"/>
|
||||||
</q-toolbar-title>
|
</q-toolbar-title>
|
||||||
|
|
||||||
<q-btn
|
|
||||||
:label="`${langs[locale.locale.value].flag} ${langs[locale.locale.value].short}`"
|
|
||||||
no-caps
|
|
||||||
color="grey-5"
|
|
||||||
rounded
|
|
||||||
>
|
|
||||||
<q-menu>
|
|
||||||
<q-list style="min-width: 100px">
|
|
||||||
<q-item v-for="(v, k) in langs" :key="k"
|
|
||||||
clickable v-close-popup @click="locale.locale.value=k">
|
|
||||||
<q-item-section>{{ v.flag }} {{ v.title }}</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-menu>
|
|
||||||
</q-btn>
|
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
round
|
round
|
||||||
flat
|
flat
|
||||||
@@ -48,13 +32,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useLoginStore } from 'stores/login'
|
import { useLoginStore } from 'stores/login'
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { langs } from 'src/lib/langlist'
|
|
||||||
|
|
||||||
const loginStore = useLoginStore()
|
const loginStore = useLoginStore()
|
||||||
const locale = useI18n({ useScope: 'global' })
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const logout = function () {
|
const logout = function () {
|
||||||
loginStore.logout()
|
loginStore.logout()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<q-page padding>
|
<q-page padding>
|
||||||
<div class="row justify-center items-center" style="height: 70vh;">
|
<div class="row justify-center items-center" style="height: 70vh;">
|
||||||
<div class="col-xs-10 col-sm-8 col-md-6 col-lg-4 col-xl-3 q-gutter-sm" >
|
<div class="col-xs-10 col-sm-8 col-md-6 col-lg-4 col-xl-3 q-gutter-sm" >
|
||||||
<q-input label="Kullanıcı Adı/ Email" outlined color="pcolor1" v-model="ldata.email"/>
|
<q-input label="Kullanıcı Adı/Email" outlined color="pcolor1" v-model="ldata.code"/>
|
||||||
<q-input
|
<q-input
|
||||||
type="password"
|
type="password"
|
||||||
label="Parola" outlined color="pcolor1" v-model="ldata.pass"/>
|
label="Parola" outlined color="pcolor1" v-model="ldata.pass"/>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ Router.beforeEach(
|
|||||||
|
|
||||||
if ((isLoggedIn) && ((to.path === '/') || (to.path === 'login'))) {
|
if ((isLoggedIn) && ((to.path === '/') || (to.path === 'login'))) {
|
||||||
return {
|
return {
|
||||||
path: '/panel',
|
path: '/',
|
||||||
query: '',
|
query: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ const routes = [
|
|||||||
requiresAuth: true
|
requiresAuth: true
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{ path: '', component: () => import('pages/IndexPage.vue') },
|
{ path: '', component: () => import('pages/index.vue') },
|
||||||
|
{ path: '/panel', component: () => import('pages/index.vue') },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,14 @@ const defaultState = {
|
|||||||
LoggedIn: false,
|
LoggedIn: false,
|
||||||
Token: '',
|
Token: '',
|
||||||
UsrKSUID: '',
|
UsrKSUID: '',
|
||||||
|
UsrEmail: '',
|
||||||
|
UsrFullname: '',
|
||||||
|
Username: '',
|
||||||
|
|
||||||
ClientKSUID: '',
|
ClientKSUID: '',
|
||||||
Email: '',
|
ClientCode: '',
|
||||||
Fullname: '',
|
LicenseCode: '',
|
||||||
|
PkgConf: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLoginStore = defineStore('login', {
|
export const useLoginStore = defineStore('login', {
|
||||||
|
|||||||
Reference in New Issue
Block a user