Re-implement the client
Not all website features are working, but this commit is mostly focused on an architecture that can be used in future projects
This commit is contained in:
parent
b8748509a4
commit
2bf57351ea
39 changed files with 5132 additions and 3635 deletions
|
@ -17,14 +17,10 @@ AllCops:
|
||||||
- lib/**/*.rb
|
- lib/**/*.rb
|
||||||
- libexec/*
|
- libexec/*
|
||||||
- libexec/*/**
|
- libexec/*/**
|
||||||
- nanoc/rules/*
|
|
||||||
- nanoc/lib/*
|
|
||||||
- bin/*
|
- bin/*
|
||||||
- test/*
|
- test/*
|
||||||
Exclude:
|
Exclude:
|
||||||
- src/css/vendor/tail.css/bin/*
|
- src/css/vendor/tail.css/bin/*
|
||||||
- src/tmp/*
|
|
||||||
- src/tmp/**/*
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Enabled
|
# Enabled
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -4,3 +4,4 @@ gem "twenty-cli", path: "./cli"
|
||||||
gem "twenty-server", path: "./server"
|
gem "twenty-server", path: "./server"
|
||||||
gem "twenty-client", path: "./client"
|
gem "twenty-client", path: "./client"
|
||||||
gem "listen"
|
gem "listen"
|
||||||
|
gem "server.rb", path: "../../libs/ruby/server.rb"
|
||||||
|
|
16
Gemfile.lock
16
Gemfile.lock
|
@ -1,3 +1,10 @@
|
||||||
|
PATH
|
||||||
|
remote: ../../libs/ruby/server.rb
|
||||||
|
specs:
|
||||||
|
server.rb (0.2.2)
|
||||||
|
puma (~> 6.3)
|
||||||
|
rack (~> 3.0)
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
|
@ -24,7 +31,6 @@ PATH
|
||||||
twenty-server (0.5.8)
|
twenty-server (0.5.8)
|
||||||
graphql (~> 2.2)
|
graphql (~> 2.2)
|
||||||
sequel (~> 5.78)
|
sequel (~> 5.78)
|
||||||
server.rb (~> 0.2.2)
|
|
||||||
sqlite3 (~> 1.6)
|
sqlite3 (~> 1.6)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
|
@ -43,11 +49,11 @@ GEM
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
rb-inotify (~> 0.9, >= 0.9.10)
|
||||||
mini_portile2 (2.8.6)
|
mini_portile2 (2.8.6)
|
||||||
nio4r (2.7.1)
|
nio4r (2.7.3)
|
||||||
paint (2.3.0)
|
paint (2.3.0)
|
||||||
puma (6.4.2)
|
puma (6.4.2)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
rack (3.0.10)
|
rack (3.1.7)
|
||||||
rake (13.2.1)
|
rake (13.2.1)
|
||||||
rb-fsevent (0.11.2)
|
rb-fsevent (0.11.2)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
|
@ -55,9 +61,6 @@ GEM
|
||||||
ryo.rb (0.5.5)
|
ryo.rb (0.5.5)
|
||||||
sequel (5.80.0)
|
sequel (5.80.0)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
server.rb (0.2.2)
|
|
||||||
puma (~> 6.3)
|
|
||||||
rack (~> 3.0)
|
|
||||||
sqlite3 (1.7.3)
|
sqlite3 (1.7.3)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
|
|
||||||
|
@ -68,6 +71,7 @@ PLATFORMS
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
listen
|
listen
|
||||||
rake (~> 13.2)
|
rake (~> 13.2)
|
||||||
|
server.rb!
|
||||||
twenty!
|
twenty!
|
||||||
twenty-cli!
|
twenty-cli!
|
||||||
twenty-client!
|
twenty-client!
|
||||||
|
|
33
README.md
33
README.md
|
@ -5,26 +5,29 @@ purpose. But it's also a place where I can experiment with
|
||||||
a different stack for the development of [web] applications
|
a different stack for the development of [web] applications
|
||||||
in Ruby. See **Design** for more info.
|
in Ruby. See **Design** for more info.
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* Provides a command-line utility to start / stop a web server
|
|
||||||
* Connect / disconnect a project from the command line
|
|
||||||
* Designed to work offline
|
|
||||||
* Lightweight stack
|
|
||||||
* Easy to install, easy to use
|
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
* The server is powered by [rack](https://github.com/rack/rack) and [puma](https://github.com/puma/puma):
|
* The [server/](server/) is powered by Ruby
|
||||||
- Accepts GraphQL requests at `/graphql`
|
- [rack](https://github.com/rack/rack#readmne),
|
||||||
- Serves client (HTML, JS, CSS)
|
[graphql-ruby](https://github.com/rmosolgo/graphql-ruby#readme),
|
||||||
- Dependencies: Sequel, SQLite3, ruby-graphql
|
and [puma](https://github.com/puma/puma#readme)
|
||||||
* The client is a statically compiled [nanoc](https://github.com/nanoc/nanoc) website:
|
- The server provides the /graphql endpoint for client <-> server communication
|
||||||
- Dependencies: webpack, typescript, react
|
- The server serves static files (HTML, JS, CSS, ...) via [puma (Ruby HTTP server)](https://github.com/puma/puma#readme)
|
||||||
* The CLI controls the web server:
|
- The /graphql endpoint enters the [graphql-ruby](https://github.com/rmosolgo/graphql-ruby#readme) stack
|
||||||
|
* The [client/](client/) is powered by NodeJS
|
||||||
|
- [webpack](https://webpack.js.org/),
|
||||||
|
[typescript](https://www.typescriptlang.org/),
|
||||||
|
[react](https://react.dev/),
|
||||||
|
and [react-router](https://reactrouter.com/en/main)
|
||||||
|
- The client produces a build/ directory
|
||||||
|
- The client provides static files (HTML, JS, CSS, ...)
|
||||||
|
- The client provides routes via [react-router](https://reactrouter.com/en/main)
|
||||||
|
- The client communicates with the server via [@apollo/client (GraphQL client)](https://www.apollographql.com/docs/react/)
|
||||||
|
* The [cli](cli/) is powered by Ruby
|
||||||
- Start / stop web server
|
- Start / stop web server
|
||||||
- Run database migrations
|
- Run database migrations
|
||||||
- Run developer console
|
- Run developer console
|
||||||
|
- Available as a RubyGem executable
|
||||||
* Each component (server, client, cli) are separate packages
|
* Each component (server, client, cli) are separate packages
|
||||||
in a monorepo
|
in a monorepo
|
||||||
* Easy to distribute as a RubyGem
|
* Easy to distribute as a RubyGem
|
||||||
|
|
|
@ -22,14 +22,10 @@ AllCops:
|
||||||
- lib/**/*.rb
|
- lib/**/*.rb
|
||||||
- libexec/*
|
- libexec/*
|
||||||
- libexec/*/**
|
- libexec/*/**
|
||||||
- nanoc/rules/*
|
|
||||||
- nanoc/lib/*
|
|
||||||
- bin/*
|
- bin/*
|
||||||
- test/*
|
- test/*
|
||||||
Exclude:
|
Exclude:
|
||||||
- src/css/vendor/tail.css/bin/*
|
- src/css/vendor/tail.css/bin/*
|
||||||
- src/tmp/*
|
|
||||||
- src/tmp/**/*
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Enabled
|
# Enabled
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
extends: ["standard-with-typescript", "standard-jsx", "prettier"],
|
|
||||||
parserOptions: {
|
|
||||||
project: "./tsconfig.json",
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
"@typescript-eslint/member-delimiter-style": 2,
|
|
||||||
"@typescript-eslint/semi": ["error", "always"],
|
|
||||||
"@typescript-eslint/no-extra-semi": "error",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": 0,
|
|
||||||
"@typescript-eslint/strict-boolean-expressions": 0,
|
|
||||||
"@typescript-eslint/no-floating-promises": 0,
|
|
||||||
"@typescript-eslint/prefer-nullish-coalescing": 0,
|
|
||||||
"@typescript-eslint/restrict-template-expressions": 0,
|
|
||||||
"@typescript-eslint/promise-function-async": 0,
|
|
||||||
"@typescript-eslint/consistent-type-definitions": 0,
|
|
||||||
"@typescript-eslint/no-misused-promises": ["error", {"checksConditionals": false}],
|
|
||||||
"@typescript-eslint/no-redeclare": 0,
|
|
||||||
"@typescript-eslint/no-non-null-assertion": 0,
|
|
||||||
"@typescript-eslint/member-delimiter-style": 0,
|
|
||||||
"no-return-assign": 0,
|
|
||||||
"no-useless-return": 0,
|
|
||||||
"quotes": 0,
|
|
||||||
"object-curly-spacing": 2,
|
|
||||||
"n/no-callback-literal": 0,
|
|
||||||
"import/no-absolute-path": 0
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -22,14 +22,10 @@ AllCops:
|
||||||
- lib/**/*.rb
|
- lib/**/*.rb
|
||||||
- libexec/*
|
- libexec/*
|
||||||
- libexec/*/**
|
- libexec/*/**
|
||||||
- nanoc/rules/*
|
|
||||||
- nanoc/lib/*
|
|
||||||
- bin/*
|
- bin/*
|
||||||
- test/*
|
- test/*
|
||||||
Exclude:
|
Exclude:
|
||||||
- src/css/vendor/tail.css/bin/*
|
- src/css/vendor/tail.css/bin/*
|
||||||
- src/tmp/*
|
|
||||||
- src/tmp/**/*
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Enabled
|
# Enabled
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
require 'rbconfig'
|
require 'rbconfig'
|
||||||
case RbConfig::CONFIG['target_os']
|
case RbConfig::CONFIG['target_os']
|
||||||
when /(?i-mx:bsd|dragonfly)/
|
when /(?i-mx:bsd|dragonfly)/
|
||||||
|
|
|
@ -6,78 +6,16 @@ PATH
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
addressable (2.8.6)
|
|
||||||
public_suffix (>= 2.0.2, < 6.0)
|
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
colored (1.2)
|
|
||||||
concurrent-ruby (1.2.3)
|
|
||||||
cri (2.15.11)
|
|
||||||
ddmetrics (1.1.0)
|
|
||||||
ddplugin (1.0.3)
|
|
||||||
diff-lcs (1.5.1)
|
|
||||||
ffi (1.16.3)
|
|
||||||
immutable-ruby (0.1.0)
|
|
||||||
concurrent-ruby (~> 1.1)
|
|
||||||
sorted_set (~> 1.0)
|
|
||||||
json (2.7.1)
|
json (2.7.1)
|
||||||
json_schema (0.21.0)
|
|
||||||
language_server-protocol (3.17.0.3)
|
language_server-protocol (3.17.0.3)
|
||||||
lint_roller (1.1.0)
|
lint_roller (1.1.0)
|
||||||
listen (3.8.0)
|
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
|
||||||
memo_wise (1.8.0)
|
|
||||||
nanoc (4.12.19)
|
|
||||||
addressable (~> 2.5)
|
|
||||||
colored (~> 1.2)
|
|
||||||
nanoc-checking (~> 1.0, >= 1.0.2)
|
|
||||||
nanoc-cli (= 4.12.19)
|
|
||||||
nanoc-core (= 4.12.19)
|
|
||||||
nanoc-deploying (~> 1.0)
|
|
||||||
parallel (~> 1.12)
|
|
||||||
tty-command (~> 0.8)
|
|
||||||
tty-which (~> 0.4)
|
|
||||||
nanoc-checking (1.0.2)
|
|
||||||
nanoc-cli (~> 4.12, >= 4.12.4)
|
|
||||||
nanoc-core (~> 4.12, >= 4.12.4)
|
|
||||||
nanoc-cli (4.12.19)
|
|
||||||
cri (~> 2.15)
|
|
||||||
diff-lcs (~> 1.3)
|
|
||||||
nanoc-core (= 4.12.19)
|
|
||||||
zeitwerk (~> 2.1)
|
|
||||||
nanoc-core (4.12.19)
|
|
||||||
concurrent-ruby (~> 1.1)
|
|
||||||
ddmetrics (~> 1.0)
|
|
||||||
ddplugin (~> 1.0)
|
|
||||||
immutable-ruby (~> 0.1)
|
|
||||||
json_schema (~> 0.19)
|
|
||||||
memo_wise (~> 1.5)
|
|
||||||
psych (>= 4.0, < 6.0)
|
|
||||||
slow_enumerator_tools (~> 1.0)
|
|
||||||
tty-platform (~> 0.2)
|
|
||||||
zeitwerk (~> 2.1)
|
|
||||||
nanoc-deploying (1.0.2)
|
|
||||||
nanoc-checking (~> 1.0)
|
|
||||||
nanoc-cli (~> 4.11, >= 4.11.15)
|
|
||||||
nanoc-core (~> 4.11, >= 4.11.15)
|
|
||||||
nanoc-webpack.rb (0.10.6)
|
|
||||||
ryo.rb (~> 0.5)
|
|
||||||
test-cmd.rb (~> 0.12.4)
|
|
||||||
parallel (1.24.0)
|
parallel (1.24.0)
|
||||||
parser (3.3.0.5)
|
parser (3.3.0.5)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
pastel (0.8.0)
|
|
||||||
tty-color (~> 0.5)
|
|
||||||
psych (5.1.2)
|
|
||||||
stringio
|
|
||||||
public_suffix (5.0.4)
|
|
||||||
racc (1.7.3)
|
racc (1.7.3)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rb-fsevent (0.11.2)
|
|
||||||
rb-inotify (0.10.1)
|
|
||||||
ffi (~> 1.0)
|
|
||||||
rbtree (0.4.6)
|
|
||||||
regexp_parser (2.9.0)
|
regexp_parser (2.9.0)
|
||||||
rexml (3.2.8)
|
rexml (3.2.8)
|
||||||
strscan (>= 3.0.9)
|
strscan (>= 3.0.9)
|
||||||
|
@ -98,12 +36,6 @@ GEM
|
||||||
rubocop (>= 1.48.1, < 2.0)
|
rubocop (>= 1.48.1, < 2.0)
|
||||||
rubocop-ast (>= 1.30.0, < 2.0)
|
rubocop-ast (>= 1.30.0, < 2.0)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ryo.rb (0.5.5)
|
|
||||||
set (1.1.0)
|
|
||||||
slow_enumerator_tools (1.1.0)
|
|
||||||
sorted_set (1.0.3)
|
|
||||||
rbtree
|
|
||||||
set (~> 1.0)
|
|
||||||
standard (1.35.1)
|
standard (1.35.1)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (~> 3.17.0.2)
|
||||||
lint_roller (~> 1.0)
|
lint_roller (~> 1.0)
|
||||||
|
@ -116,16 +48,8 @@ GEM
|
||||||
standard-performance (1.3.1)
|
standard-performance (1.3.1)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop-performance (~> 1.20.2)
|
rubocop-performance (~> 1.20.2)
|
||||||
stringio (3.1.0)
|
|
||||||
strscan (3.1.0)
|
strscan (3.1.0)
|
||||||
test-cmd.rb (0.12.4)
|
|
||||||
tty-color (0.6.0)
|
|
||||||
tty-command (0.10.1)
|
|
||||||
pastel (~> 0.8)
|
|
||||||
tty-platform (0.3.0)
|
|
||||||
tty-which (0.5.0)
|
|
||||||
unicode-display_width (2.5.0)
|
unicode-display_width (2.5.0)
|
||||||
zeitwerk (2.6.13)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
amd64-freebsd-14
|
amd64-freebsd-14
|
||||||
|
@ -133,9 +57,6 @@ PLATFORMS
|
||||||
x86_64-openbsd
|
x86_64-openbsd
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
listen (~> 3.8)
|
|
||||||
nanoc (~> 4.12)
|
|
||||||
nanoc-webpack.rb (~> 0.10.6)
|
|
||||||
standard (~> 1.35)
|
standard (~> 1.35)
|
||||||
twenty-client!
|
twenty-client!
|
||||||
|
|
||||||
|
|
26
client/Rules
26
client/Rules
|
@ -1,26 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "nanoc-webpack"
|
|
||||||
|
|
||||||
def require_rules(rules, locals = {}, target = binding)
|
|
||||||
locals.each { target.local_variable_set(_1, _2) }
|
|
||||||
path = File.join(Dir.getwd, rules)
|
|
||||||
target.eval(
|
|
||||||
if File.readable?(path)
|
|
||||||
File.read(path)
|
|
||||||
elsif File.readable?("#{path}.rb")
|
|
||||||
File.read("#{path}.rb")
|
|
||||||
elsif File.readable?("#{path}.rules")
|
|
||||||
File.read("#{path}.rules")
|
|
||||||
else
|
|
||||||
raise LoadError, "#{path} is not readable"
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
require_rules "nanoc/rules/assets"
|
|
||||||
require_rules "nanoc/rules/react"
|
|
||||||
|
|
||||||
compile("/**/*") { write(nil) }
|
|
||||||
layout "/**/*", :erb
|
|
|
@ -16,9 +16,6 @@ Gem::Specification.new do |gem|
|
||||||
gem.description = "#{gem.summary}. " \
|
gem.description = "#{gem.summary}. " \
|
||||||
"Static content (HTML, CSS, JS). " \
|
"Static content (HTML, CSS, JS). " \
|
||||||
"See https://rubygems.org/gems/twenty for context."
|
"See https://rubygems.org/gems/twenty for context."
|
||||||
gem.add_development_dependency "nanoc", "~> 4.12"
|
|
||||||
gem.add_development_dependency "nanoc-webpack.rb", "~> 0.10.6"
|
|
||||||
gem.add_development_dependency "listen", "~> 3.8"
|
|
||||||
gem.add_development_dependency "standard", "~> 1.35"
|
gem.add_development_dependency "standard", "~> 1.35"
|
||||||
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/twenty#readme" }
|
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/twenty#readme" }
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,9 +16,6 @@ Gem::Specification.new do |gem|
|
||||||
gem.description = "#{gem.summary}. " \
|
gem.description = "#{gem.summary}. " \
|
||||||
"Static content (HTML, CSS, JS). " \
|
"Static content (HTML, CSS, JS). " \
|
||||||
"See https://rubygems.org/gems/<%= parent %> for context."
|
"See https://rubygems.org/gems/<%= parent %> for context."
|
||||||
gem.add_development_dependency "nanoc", "~> 4.12"
|
|
||||||
gem.add_development_dependency "nanoc-webpack.rb", "~> 0.10.6"
|
|
||||||
gem.add_development_dependency "listen", "~> 3.8"
|
|
||||||
gem.add_development_dependency "standard", "~> 1.35"
|
gem.add_development_dependency "standard", "~> 1.35"
|
||||||
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/<%= parent %>#readme" }
|
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/<%= parent %>#readme" }
|
||||||
end
|
end
|
||||||
|
|
16
client/eslint.config.mjs
Normal file
16
client/eslint.config.mjs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import eslint from '@eslint/js';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
import prettier from 'eslint-plugin-prettier/recommended';
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ignores: ["src/js/types/schema.ts"]},
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
prettier,
|
||||||
|
{
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-require-imports': 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
|
@ -1,23 +0,0 @@
|
||||||
# A list of file extensions that Nanoc will consider to be textual rather than
|
|
||||||
# binary. If an item with an extension not in this list is found, the file
|
|
||||||
# will be considered as binary.
|
|
||||||
text_extensions: [ 'adoc', 'asciidoc', 'atom', 'coffee', 'css', 'erb', 'haml', 'handlebars', 'hb', 'htm', 'html', 'js', 'less', 'markdown', 'md', 'ms', 'mustache', 'php', 'rb', 'rdoc', 'sass', 'scss', 'slim', 'tex', 'txt', 'xhtml', 'xml', 'ts', 'tsx' ]
|
|
||||||
|
|
||||||
prune:
|
|
||||||
auto_prune: true
|
|
||||||
|
|
||||||
lib_dirs: ['nanoc/lib']
|
|
||||||
output_dir: build/
|
|
||||||
|
|
||||||
data_sources:
|
|
||||||
- type: filesystem
|
|
||||||
encoding: utf-8
|
|
||||||
content_dir: src/
|
|
||||||
layouts_dir: src/layouts
|
|
||||||
|
|
||||||
server:
|
|
||||||
unix:
|
|
||||||
path: /tmp/github.com.0x1eef.twenty
|
|
||||||
tcp:
|
|
||||||
host: 127.0.0.1
|
|
||||||
port: 7777
|
|
|
@ -1,10 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
compile("/fonts/*.ttf") do
|
|
||||||
write(item.identifier.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
compile("/favicon.svg") do
|
|
||||||
write(item.identifier.to_s)
|
|
||||||
end
|
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
{
|
|
||||||
"Task" => ["tasks/new", "tasks/edit"],
|
|
||||||
"Tasks" => ["/", "tasks"],
|
|
||||||
"Projects" => ["projects"]
|
|
||||||
}.each do |component, paths|
|
|
||||||
compile "/html/react.html.erb", rep: component do
|
|
||||||
filter(:erb, locals: {component: "react-#{component.downcase}", src: "/js/main.js"})
|
|
||||||
paths.each do |path|
|
|
||||||
(path == "/") ? write("/index.html") : write("/#{path}/index.html")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
compile "/js/main/main.tsx" do
|
|
||||||
buildenv = ENV["buildenv"] || "development"
|
|
||||||
filter(
|
|
||||||
:webpack,
|
|
||||||
argv: ["--mode", buildenv, "--config", "webpack.#{buildenv}.js"],
|
|
||||||
depend_on: %w[/js/components /js/hooks /js/types /js/lib /css]
|
|
||||||
)
|
|
||||||
write("/js/main.js")
|
|
||||||
end
|
|
8206
client/package-lock.json
generated
8206
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,35 +1,41 @@
|
||||||
{
|
{
|
||||||
"name": "twenty",
|
"name": "twenty",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@apollo/client": "^3.3.21",
|
"@apollo/client": "^3.3",
|
||||||
"@graphql-codegen/cli": "^5.0.0",
|
"@graphql-codegen/cli": "^5.0",
|
||||||
"@graphql-codegen/typescript": "^4.0.1",
|
"@graphql-codegen/typescript": "^4.0",
|
||||||
"@graphql-codegen/typescript-resolvers": "^4.0.1",
|
"@graphql-codegen/typescript-resolvers": "^4.0",
|
||||||
"@types/luxon": "^3.3.7",
|
"@types/luxon": "^3.3",
|
||||||
"@types/react": "^18.0.18",
|
"@types/react": "^18.0",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0",
|
||||||
"@types/showdown": "^2.0.6",
|
"@types/showdown": "^2.0",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3",
|
||||||
"css-loader": "^7.1.2",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"esbuild-loader": "^4.1.0",
|
"copy-webpack-plugin": "^12.0",
|
||||||
"eslint": "^8.26.0",
|
"css-loader": "^7.1",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"esbuild-loader": "^4.1",
|
||||||
"graphql": "^16.8.1",
|
"eslint": "^9.8",
|
||||||
"luxon": "^3.4.4",
|
"eslint-config-prettier": "^9.1",
|
||||||
"prettier": "^2.7.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"react": "^18.2.0",
|
"graphql": "^16.8",
|
||||||
"react-dom": "^18.2.0",
|
"html-webpack-plugin": "^5.6",
|
||||||
"react-hook-form": "^7.49.2",
|
"luxon": "^3.4",
|
||||||
"sass": "^1.77.8",
|
"prettier": "^3.3",
|
||||||
"sass-loader": "^16.0.0",
|
"react": "^18.2",
|
||||||
"showdown": "^2.1.0",
|
"react-dom": "^18.2",
|
||||||
"style-loader": "^4.0.0",
|
"react-hook-form": "^7.49",
|
||||||
"ts-standard": "^12.0.1",
|
"react-router-dom": "^6.25.1",
|
||||||
"tslib": "^2.2.0",
|
"sass": "^1.77",
|
||||||
"typescript": "^4.8.2",
|
"sass-loader": "^16.0",
|
||||||
"webpack": "^5.91.0",
|
"showdown": "^2.1",
|
||||||
"webpack-cli": "^4.10.0",
|
"style-loader": "^4.0",
|
||||||
"webpack-merge": "^5.10.0"
|
"tslib": "^2.2",
|
||||||
|
"typescript": "^5.5",
|
||||||
|
"typescript-eslint": "^8.0.0-alpha.58",
|
||||||
|
"webpack": "^5.93",
|
||||||
|
"webpack-cli": "^5.1",
|
||||||
|
"webpack-dev-server": "^5.0",
|
||||||
|
"webpack-merge": "^6.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"eslint": "npm exec eslint -- --fix src/js/",
|
"eslint": "npm exec eslint -- --fix src/js/",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="icon" href="/favicon.svg"/>
|
<link rel="icon" href="/favicon.svg"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="<%= component %> w-full wrapper font-sans"></div>
|
<div class="w-full wrapper font-sans react-app"></div>
|
||||||
<script src="<%= src %>"></script>
|
<script src="<%= mainjs %>"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,8 +1,12 @@
|
||||||
import { PropsWithChildren } from "react";
|
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||||
|
import React from "react";
|
||||||
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
|
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
|
||||||
|
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
|
||||||
import { AppContext } from "~/Context";
|
import { AppContext } from "~/Context";
|
||||||
|
import { Tasks } from "~/components/Tasks";
|
||||||
|
import { Task } from "~/components/Task";
|
||||||
|
|
||||||
export function App({ children }: PropsWithChildren<{}>) {
|
export function App() {
|
||||||
const client = new ApolloClient({
|
const client = new ApolloClient({
|
||||||
uri: "/graphql",
|
uri: "/graphql",
|
||||||
cache: new InMemoryCache(),
|
cache: new InMemoryCache(),
|
||||||
|
@ -16,6 +20,24 @@ export function App({ children }: PropsWithChildren<{}>) {
|
||||||
const cookies = Object.fromEntries(
|
const cookies = Object.fromEntries(
|
||||||
document.cookie.split(";").map(e => e.split("=")),
|
document.cookie.split(";").map(e => e.split("=")),
|
||||||
);
|
);
|
||||||
|
const router = createBrowserRouter([
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
element: <Tasks />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tasks",
|
||||||
|
element: <Tasks />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tasks/new",
|
||||||
|
element: <Task />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tasks/edit",
|
||||||
|
element: <Task />
|
||||||
|
}
|
||||||
|
]);
|
||||||
/* allowlist: param keys acceptable as cookie keys */
|
/* allowlist: param keys acceptable as cookie keys */
|
||||||
const allowlist = ["projectId"];
|
const allowlist = ["projectId"];
|
||||||
Object.entries(params).forEach(([key, value]) => {
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
@ -27,9 +49,12 @@ export function App({ children }: PropsWithChildren<{}>) {
|
||||||
document.cookie = `${key}=${value}; path=/`;
|
document.cookie = `${key}=${value}; path=/`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
loadDevMessages();
|
||||||
|
loadErrorMessages();
|
||||||
return (
|
return (
|
||||||
<AppContext.Provider value={{ params, cookies }}>
|
<ApolloProvider client={client}>
|
||||||
<ApolloProvider client={client}>{children}</ApolloProvider>
|
<AppContext.Provider value={{ params, cookies }}/>
|
||||||
</AppContext.Provider>
|
<RouterProvider router={router} />
|
||||||
|
</ApolloProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import React from "react";
|
||||||
import type { Task } from "~/types/schema";
|
import type { Task } from "~/types/schema";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
import { DateTime } from "luxon";
|
import { DateTime } from "luxon";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { AppContext } from "~/Context";
|
import { AppContext } from "~/Context";
|
||||||
import { Maybe } from "~/types/schema";
|
import { Maybe } from "~/types/schema";
|
||||||
import { ProjectSelect } from "~/components/ProjectSelect";
|
import { ProjectSelect } from "~/components/ProjectSelect";
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import React from "react";
|
||||||
import { useProjects } from "~/hooks/queries/useProjects";
|
import { useProjects } from "~/hooks/queries/useProjects";
|
||||||
import { Project, Maybe } from "~/types/schema";
|
import { Project, Maybe } from "~/types/schema";
|
||||||
import { Select, Option } from "~/components/Select";
|
import { Select, Option } from "~/components/Select";
|
||||||
|
@ -47,5 +48,4 @@ export function ProjectSelect({ onChange, selected }: Props) {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
1;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ReactNode, useState, useEffect } from "react";
|
import React, { ReactNode, useState, useEffect } from "react";
|
||||||
import { Filter } from "./Filter";
|
import { Filter } from "./Filter";
|
||||||
|
|
||||||
const LI_CLASSNAME = [
|
const LI_CLASSNAME = [
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
|
|
||||||
export type Tab = {
|
export type Tab = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useState, useContext } from "react";
|
import React, { useEffect, useState, useContext } from "react";
|
||||||
import { AppContext } from "~/Context";
|
import { AppContext } from "~/Context";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useCreateTask } from "~/hooks/mutations/useCreateTask";
|
import { useCreateTask } from "~/hooks/mutations/useCreateTask";
|
||||||
|
@ -35,7 +35,11 @@ export function Task() {
|
||||||
const res = await createTask({ variables: { input } });
|
const res = await createTask({ variables: { input } });
|
||||||
const payload = res?.data?.createTask;
|
const payload = res?.data?.createTask;
|
||||||
const { errors } = payload;
|
const { errors } = payload;
|
||||||
errors.length ? alert(errors) : (location.href = "/tasks");
|
if (errors.length) {
|
||||||
|
alert(errors);
|
||||||
|
} else {
|
||||||
|
location.href = "/tasks";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +82,11 @@ export function Task() {
|
||||||
labels={["Editor", "Preview"]}
|
labels={["Editor", "Preview"]}
|
||||||
defaultLabel={taskId ? "preview" : "editor"}
|
defaultLabel={taskId ? "preview" : "editor"}
|
||||||
onChange={(tab: Tab) => {
|
onChange={(tab: Tab) => {
|
||||||
tab.id === "editor" ? setIsEditable(true) : setIsEditable(false);
|
if (tab.id === "editor") {
|
||||||
|
setIsEditable(true);
|
||||||
|
} else {
|
||||||
|
setIsEditable(false);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{isEditable ? (
|
{isEditable ? (
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import React from "react";
|
||||||
import { Task, TaskStatus } from "~/types/schema";
|
import { Task, TaskStatus } from "~/types/schema";
|
||||||
import { useUpdateTask } from "~/hooks/mutations/useUpdateTask";
|
import { useUpdateTask } from "~/hooks/mutations/useUpdateTask";
|
||||||
import { GET_TASKS } from "~/hooks/queries/useTasks";
|
import { GET_TASKS } from "~/hooks/queries/useTasks";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useContext } from "react";
|
import React, { useEffect, useContext } from "react";
|
||||||
import { AppContext } from "~/Context";
|
import { AppContext } from "~/Context";
|
||||||
import { NavBar } from "~/components/NavBar";
|
import { NavBar } from "~/components/NavBar";
|
||||||
import { Group } from "~/components/Group";
|
import { Group } from "~/components/Group";
|
||||||
|
|
|
@ -1,36 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ReactDOM from "react-dom/client";
|
import ReactDOM from "react-dom/client";
|
||||||
import { App } from "~/components/App";
|
import { App } from "~/components/App";
|
||||||
import { Tasks } from "~/components/Tasks";
|
|
||||||
import { Projects } from "~/components/Projects";
|
|
||||||
import { Task } from "~/components/Task";
|
|
||||||
import "@css/main.scss";
|
import "@css/main.scss";
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
const components = {
|
const root = document.querySelector(".react-app");
|
||||||
Task: () => (
|
ReactDOM.createRoot(root).render(<App/>);
|
||||||
<App>
|
|
||||||
<Task />
|
|
||||||
</App>
|
|
||||||
),
|
|
||||||
Tasks: () => (
|
|
||||||
<App>
|
|
||||||
<Tasks />
|
|
||||||
</App>
|
|
||||||
),
|
|
||||||
Projects: () => (
|
|
||||||
<App>
|
|
||||||
<Projects />
|
|
||||||
</App>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
const ents = Object.entries(components);
|
|
||||||
for (let i = 0; i < ents.length; i++) {
|
|
||||||
const [component, getJSX] = ents[i];
|
|
||||||
const root = document.querySelector(`.react-${component.toLowerCase()}`);
|
|
||||||
if (root) {
|
|
||||||
ReactDOM.createRoot(root).render(getJSX());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
const webpack = require('webpack');
|
const webpack = require("webpack");
|
||||||
const path = require('path');
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
entry: path.resolve(__dirname, "src/js/main/main.tsx"),
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "build"),
|
||||||
|
filename: "static/js/main.js"
|
||||||
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'~': [path.resolve('src/js')],
|
"~": [path.resolve(__dirname, "src/js")],
|
||||||
'@css': [path.resolve('src/css')]
|
"@css": [path.resolve(__dirname, "src/css")]
|
||||||
},
|
},
|
||||||
extensions: ['.ts', '.tsx', '.scss']
|
extensions: [".ts", ".tsx", ".scss"]
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
loader: 'esbuild-loader',
|
loader: "esbuild-loader",
|
||||||
options: {
|
options: {
|
||||||
loader: 'tsx',
|
loader: "tsx",
|
||||||
target: 'es2015'
|
target: "es2015"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -31,8 +39,9 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.ProvidePlugin({
|
new CleanWebpackPlugin(),
|
||||||
React: 'react',
|
new CopyWebpackPlugin({patterns: [
|
||||||
}),
|
{from: "./src/favicon.svg", to: "favicon.ico"}
|
||||||
|
]}),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
const { merge } = require('webpack-merge');
|
const { merge } = require('webpack-merge');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const common = require('./webpack.common.js');
|
const common = require('./webpack.common.js');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = merge(
|
module.exports = merge(
|
||||||
common,
|
common,
|
||||||
{mode: "development"}
|
{ mode: "development", devServer: {static: './build'} },
|
||||||
|
{ plugins: [new HtmlWebpackPlugin({
|
||||||
|
inject: false,
|
||||||
|
templateParameters: {
|
||||||
|
mainjs: '/static/js/main.js'
|
||||||
|
}
|
||||||
|
})]},
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log(module.exports)
|
||||||
|
|
|
@ -3,18 +3,17 @@
|
||||||
cwd = File.realpath File.join(__dir__, "..", "..", "..", "client")
|
cwd = File.realpath File.join(__dir__, "..", "..", "..", "client")
|
||||||
desc "Start web server"
|
desc "Start web server"
|
||||||
task :server, [:protocol] do |_t, args|
|
task :server, [:protocol] do |_t, args|
|
||||||
nanoc = Ryo.from_yaml(path: File.join(cwd, "nanoc.yaml"))
|
|
||||||
h = args.to_h
|
h = args.to_h
|
||||||
p = h[:protocol] || "tcp"
|
p = h[:protocol] || "tcp"
|
||||||
n = File.basename File.dirname(cwd)
|
n = File.basename File.dirname(cwd)
|
||||||
Process.setproctitle "rake server[#{p}] [#{n}]"
|
Process.setproctitle "rake server[#{p}] [#{n}]"
|
||||||
if p == "unix"
|
if p == "unix"
|
||||||
Twenty::Command::Up
|
Twenty::Command::Up
|
||||||
.new(["-u", nanoc.server.unix.path])
|
.new(["-u", "/tmp/www/twenty.al-ridwan.home.network"])
|
||||||
.run
|
.run
|
||||||
else
|
else
|
||||||
Twenty::Command::Up
|
Twenty::Command::Up
|
||||||
.new(["-b", nanoc.server.tcp.host, "-p", nanoc.server.tcp.port])
|
.new(["-b", "127.0.0.1", "-p", "2222"])
|
||||||
.run
|
.run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,14 +22,10 @@ AllCops:
|
||||||
- lib/**/*.rb
|
- lib/**/*.rb
|
||||||
- libexec/*
|
- libexec/*
|
||||||
- libexec/*/**
|
- libexec/*/**
|
||||||
- nanoc/rules/*
|
|
||||||
- nanoc/lib/*
|
|
||||||
- bin/*
|
- bin/*
|
||||||
- test/*
|
- test/*
|
||||||
Exclude:
|
Exclude:
|
||||||
- src/css/vendor/tail.css/bin/*
|
- src/css/vendor/tail.css/bin/*
|
||||||
- src/tmp/*
|
|
||||||
- src/tmp/**/*
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Enabled
|
# Enabled
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
gem 'server.rb', path: '../../../libs/ruby/server.rb'
|
||||||
gemspec
|
gemspec
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
|
PATH
|
||||||
|
remote: ../../../libs/ruby/server.rb
|
||||||
|
specs:
|
||||||
|
server.rb (0.2.2)
|
||||||
|
puma (~> 6.3)
|
||||||
|
rack (~> 3.0)
|
||||||
|
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
twenty-server (0.5.8)
|
twenty-server (0.5.8)
|
||||||
graphql (~> 2.2)
|
graphql (~> 2.2)
|
||||||
sequel (~> 5.78)
|
sequel (~> 5.78)
|
||||||
server.rb (~> 0.2.2)
|
|
||||||
sqlite3 (~> 1.6)
|
sqlite3 (~> 1.6)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
|
@ -52,9 +58,6 @@ GEM
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
sequel (5.78.0)
|
sequel (5.78.0)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
server.rb (0.2.2)
|
|
||||||
puma (~> 6.3)
|
|
||||||
rack (~> 3.0)
|
|
||||||
sqlite3 (1.7.3)
|
sqlite3 (1.7.3)
|
||||||
mini_portile2 (~> 2.8.0)
|
mini_portile2 (~> 2.8.0)
|
||||||
sqlite3 (1.7.3-aarch64-linux)
|
sqlite3 (1.7.3-aarch64-linux)
|
||||||
|
@ -91,6 +94,7 @@ PLATFORMS
|
||||||
x86_64-linux
|
x86_64-linux
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
server.rb!
|
||||||
standard (~> 1.35)
|
standard (~> 1.35)
|
||||||
test-unit (~> 3.5.7)
|
test-unit (~> 3.5.7)
|
||||||
twenty-server!
|
twenty-server!
|
||||||
|
|
|
@ -2,30 +2,54 @@
|
||||||
|
|
||||||
module Twenty::Rack
|
module Twenty::Rack
|
||||||
module GraphQL
|
module GraphQL
|
||||||
|
extend self
|
||||||
|
STATIC = [%r|/static|, %r|/favicon.ico|]
|
||||||
|
|
||||||
##
|
##
|
||||||
# Extends {Server::Dir Server::Dir} (a static file
|
|
||||||
# Rack application) with a /graphql endpoint
|
|
||||||
#
|
|
||||||
# @param [Hash] env
|
# @param [Hash] env
|
||||||
# Environment hash
|
# Environment hash
|
||||||
#
|
# @return [[Integer, Hash, #each]
|
||||||
# @return [Array<Integer, Hash, #each>]
|
|
||||||
# Returns a response
|
# Returns a response
|
||||||
def call(env)
|
def call(env)
|
||||||
req = Rack::Request.new(env)
|
req = Rack::Request.new(env)
|
||||||
if req.post? &&
|
if req.post?
|
||||||
req.path == "/graphql"
|
graphql(req)
|
||||||
|
elsif req.get? || req.head?
|
||||||
|
file(req)
|
||||||
|
else
|
||||||
|
[404, {}, "".each_line]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def graphql(req)
|
||||||
|
if req.path =~ %r|/graphql|
|
||||||
params = JSON.parse(req.body.string)
|
params = JSON.parse(req.body.string)
|
||||||
result = Twenty::GraphQL::Schema.execute(
|
body = Twenty::GraphQL::Schema.execute(
|
||||||
params["query"],
|
params["query"],
|
||||||
variables: params["variables"],
|
variables: params["variables"],
|
||||||
context: {}
|
context: {}
|
||||||
)
|
).to_json
|
||||||
[200, {"content-type" => "application/json"}, [result.to_json]]
|
head = { "content-length" => body.bytesize, "content-type" => "application/json" }
|
||||||
|
[200, head, body.each_line]
|
||||||
else
|
else
|
||||||
super(env)
|
body = { errors: ["Request path was not found"] }.to_json
|
||||||
|
head = { "content-length" => body.bytesize, "content-type" => "application/json" }
|
||||||
|
[404, {}, body.each_line]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def file(req)
|
||||||
|
if STATIC.find { req.path =~ _1 }
|
||||||
|
dir = Server::Dir.new(Twenty.build)
|
||||||
|
dir.call(req.env)
|
||||||
|
else
|
||||||
|
path = File.join(Twenty.build, "index.html")
|
||||||
|
body = File.binread(path)
|
||||||
|
head = { "content-length" => body.bytesize, "content-type" => "text/html" }
|
||||||
|
[200, head, body.each_line]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Server::Dir.prepend(self)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,9 +7,11 @@ module Twenty::Rack
|
||||||
##
|
##
|
||||||
# @param [Hash, #to_h] options
|
# @param [Hash, #to_h] options
|
||||||
# Hash of server options
|
# Hash of server options
|
||||||
#
|
|
||||||
# @return [Thread]
|
# @return [Thread]
|
||||||
def self.server(options = {})
|
def self.server(options = {})
|
||||||
Server.dir(Twenty.build, options.to_h)
|
Server.new Rack::Builder.app {
|
||||||
|
use Server::ETag
|
||||||
|
run Twenty::Rack::GraphQL
|
||||||
|
}, Server.prepare(options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
||||||
gem.add_runtime_dependency "sequel", "~> 5.78"
|
gem.add_runtime_dependency "sequel", "~> 5.78"
|
||||||
gem.add_runtime_dependency "sqlite3", "~> 1.6"
|
gem.add_runtime_dependency "sqlite3", "~> 1.6"
|
||||||
gem.add_runtime_dependency "graphql", "~> 2.2"
|
gem.add_runtime_dependency "graphql", "~> 2.2"
|
||||||
gem.add_runtime_dependency "server.rb", "~> 0.2.2"
|
#gem.add_runtime_dependency "server.rb", "~> 0.2"
|
||||||
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
||||||
gem.add_development_dependency "standard", "~> 1.35"
|
gem.add_development_dependency "standard", "~> 1.35"
|
||||||
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/twenty#readme" }
|
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/twenty#readme" }
|
||||||
|
|
|
@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
||||||
gem.add_runtime_dependency "sequel", "~> 5.78"
|
gem.add_runtime_dependency "sequel", "~> 5.78"
|
||||||
gem.add_runtime_dependency "sqlite3", "~> 1.6"
|
gem.add_runtime_dependency "sqlite3", "~> 1.6"
|
||||||
gem.add_runtime_dependency "graphql", "~> 2.2"
|
gem.add_runtime_dependency "graphql", "~> 2.2"
|
||||||
gem.add_runtime_dependency "server.rb", "~> 0.2.2"
|
#gem.add_runtime_dependency "server.rb", "~> 0.2"
|
||||||
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
gem.add_development_dependency "test-unit", "~> 3.5.7"
|
||||||
gem.add_development_dependency "standard", "~> 1.35"
|
gem.add_development_dependency "standard", "~> 1.35"
|
||||||
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/<%= parent %>#readme" }
|
gem.metadata = { "source_code_uri" => "https://github.com/0x1eef/<%= parent %>#readme" }
|
||||||
|
|
Loading…
Reference in a new issue