Init Gitea

This commit is contained in:
dqj
2025-10-06 21:31:13 +09:00
commit c879b8de12
60 changed files with 41314 additions and 0 deletions

17
.env Normal file
View File

@@ -0,0 +1,17 @@
OPENAI_API_KEY=sk-proj-lC109tcQFH1uR45E78KCT3BlbkFJ1MejxE8tOVBeujj4bq2Z
PORT=8065
USER_SESSION_TIMEOUT=14400
MYSQL_HOST=127.0.0.1
MYSQL_USER=root
MYSQL_PASSWD=dqj_1998
MYSQL_DATABASE=sisai_world_db
MYSQL_POOL_LIMIT=10
SMTP_HOST=mail.amipro.me
SMTP_PORT=465
SMTP_USER=support@amipro.me
SMTP_PASS=2@kSK6rGlWvb
SMTP_FROM=support@amipro.me
REG_LINK_TIMEOUT=15
INVITE_LINK_TIMEOUT=20
REG_LINK_PREFIX=https://mac.dqj-home.com/user.html?rid=
INVITE_LINK_PREFIX=https://mac.dqj-home.com/user.html?nid=

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store
node_modules

17
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "SisAiWorld",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/app.js"
}
]
}

101
SQLs/Sample.json Normal file
View File

@@ -0,0 +1,101 @@
{
"info_blocks": [
{
"id": "1",
"info": {
"ja": "会社情報",
"en-US": "Company Info"
},
"cmmt": {
"ja": "会社全体の情報、他人に説明する時のオーバービューの様な情報",
"en-US": "Company-wide information, overview information for explaining to others"
}
},
{
"id": "2",
"info": {
"ja": "従業員情報",
"en-US": "Employee Info"
}
},
{
"id": "3",
"info": {
"ja": "商品情報",
"en-US": "Product Info"
}
},
{
"id": "4",
"info": {
"ja": "顧客情報",
"en-US": "Customer Info"
}
},
{
"id": "5",
"info": {
"ja": "取引情報",
"en-US": "Transaction Info"
}
}
],
"prompts": [
{
"type": {
"ja": "経営関連",
"en-US": "Management related"
},
"prompts": [
{
"id": "1",
"header": {
"ja": "会社の経営者として",
"en-US": "As a company executive"
},
"hint": {
"ja": "質問の50%を適当の数字へ変更してください",
"en-US": "Change 50% of the question to an appropriate number"
},
"show": {
"ja": "利益を50%増加できるために従業員が何人を増加雇用すべき?",
"en-US": "How many more employees should be hired to increase profits by 50%?"
},
"footer": {
"ja": "テーブルの形で以上の問題を回答して",
"en-US": "Answer the above question in table form"
}
},
{
"id": "2",
"show": {
"ja": "セールス一人毎の平均売上は?",
"en-US": "What is the average sale per salesperson?"
}
}
]
},
{
"type": {
"ja": "財務関連",
"en-US": "Financial related"
},
"prompts": [
{
"id": "3",
"show": {
"ja": "2024年月の売上は",
"en-US": "Sales in May 2024"
}
},
{
"id": "4",
"show": {
"ja": "毎月レストランの売上比率",
"en-US": "Monthly restaurant sales ratio"
}
}
]
}
]
}

View File

@@ -0,0 +1,218 @@
CREATE DATABASE sisai_world_db default character set utf8mb4;
use sisai_world_db;
CREATE TABLE `accounts` (
`account_id` int unsigned NOT NULL AUTO_INCREMENT,
`account_email` varchar(320) NOT NULL,
`account_pw` varchar(100) NOT NULL,
`account_name` varchar(512), -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`account_id`),
KEY `accounts_IDX1` (`created`),
KEY `accounts_IDX2` (`deleted`),
KEY `accounts_IDX3` (`account_email`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='accounts';
CREATE TABLE `registration_sessions` (
`session_id` char(40) NOT NULL,
`username` varchar(320) NOT NULL, -- can save email addresses
`created` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`session_id`),
KEY `registration_sessions_IDX1` (`created`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='registration sessions';
CREATE TABLE `space_templates` (
`spacetemp_id` int unsigned NOT NULL AUTO_INCREMENT,
`spacetemp_name` varchar(512) NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`spacetemp_intro` varchar(1024) NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }... vector search in the future
`account_id` int unsigned NOT NULL, -- creator
`shared` TINYINT default 0, -- 1: shared, 0: private
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`spacetemp_id`),
KEY `space_templates_IDX1` (`spacetemp_name`),
KEY `space_templates_IDX2` (`created`),
KEY `space_templates_IDX3` (`deleted`),
KEY `space_templates_IDX4` (`shared`),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='space templates';
CREATE TABLE `spacetplt_info_blocks` (
`info_block_id` int unsigned NOT NULL AUTO_INCREMENT,
`spacetemp_id` int unsigned NOT NULL,
`block_name` varchar(512) NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`block_cmmt` varchar(1024), -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`info_block_id`),
KEY `spacetplt_info_blocks_IDX1` (`created`),
KEY `spacetplt_info_blocks_IDX2` (`deleted`),
FOREIGN KEY (spacetemp_id) REFERENCES space_templates(spacetemp_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='space template info blocks';
CREATE TABLE `spacetplt_prompts` (
`prompt_id` int unsigned NOT NULL AUTO_INCREMENT,
`spacetemp_id` int unsigned NOT NULL,
`prompt_head` varchar(1024), -- JSON string, { "ja": "日本語", "en-US": "English" }...
`prompt_hint` varchar(1024), -- JSON string, { "ja": "日本語", "en-US": "English" }...
`prompt_show` varchar(1024) NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`prompt_foot` varchar(1024), -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`prompt_id`),
KEY `spacetplt_info_blocks_IDX1` (`created`),
KEY `spacetplt_info_blocks_IDX2` (`deleted`),
FOREIGN KEY (spacetemp_id) REFERENCES space_templates(spacetemp_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='space template prompts';
CREATE TABLE `spaces` (
`space_id` int unsigned NOT NULL AUTO_INCREMENT,
`spacetemp_id` int unsigned NOT NULL,
`space_name` varchar(512) NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`space_id`),
KEY `spaces_IDX1` (`created`),
KEY `spaces_IDX2` (`deleted`),
FOREIGN KEY (spacetemp_id) REFERENCES space_templates(spacetemp_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='spaces';
CREATE TABLE `account_permission` (
`accpermission_id` int unsigned NOT NULL AUTO_INCREMENT,
`account_id` int unsigned NOT NULL,
`space_id` int unsigned NOT NULL,
`is_admin` bit(1) default 0, -- admin can delete space
`permission` varchar(512), -- JSON string, { "info_id1": "0", "info_id2": "2" }...; 0: deny 1: view 2: read/write; info_id is id in space template
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`accpermission_id`),
KEY `account_permission_IDX1` (`created`),
KEY `account_permission_IDX2` (`deleted`),
FOREIGN KEY (space_id) REFERENCES spaces(space_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='account permission';
CREATE TABLE `accspace_info` (
`accspace_info_id` int unsigned NOT NULL AUTO_INCREMENT,
`account_id` int unsigned NOT NULL,
`space_id` int unsigned NOT NULL,
`info_id` varchar(512) NOT NULL, -- info_blocks ids of space templates
`info` text NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`accspace_info_id`),
KEY `accspace_info_IDX1` (`created`),
KEY `accspace_info_IDX2` (`deleted`),
KEY `accspace_info_IDX3` (`account_id`, `space_id`),
KEY `accspace_info_IDX4` (`info_id`),
FOREIGN KEY (account_id) REFERENCES accounts(account_id),
FOREIGN KEY (space_id) REFERENCES spaces(space_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='accspace_info';
CREATE TABLE `accspace_prompts` (
`accspace_prompts_id` int unsigned NOT NULL AUTO_INCREMENT,
`account_id` int unsigned NOT NULL,
`space_id` int unsigned NOT NULL,
`prompt` text NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`accspace_prompts_id`),
KEY `accspace_prompts_IDX1` (`created`),
KEY `accspace_prompts_IDX2` (`deleted`),
KEY `accspace_prompts_IDX3` (`account_id`, `space_id`),
FOREIGN KEY (account_id) REFERENCES accounts(account_id),
FOREIGN KEY (space_id) REFERENCES spaces(space_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='account space prompts';
CREATE TABLE `accspace_sessions` (
`accspace_sessions_id` bigint unsigned NOT NULL AUTO_INCREMENT,
`account_id` int unsigned NOT NULL,
`space_id` int unsigned NOT NULL,
`short_name` varchar(512) NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`updated` datetime DEFAULT NULL,
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`accspace_sessions_id`),
KEY `accspace_sessions_IDX1` (`created`),
KEY `accspace_sessions_IDX2` (`deleted`),
KEY `accspace_sessions_IDX3` (`account_id`, `space_id`),
KEY `accspace_sessions_IDX4` (`updated`),
FOREIGN KEY (account_id) REFERENCES accounts(account_id),
FOREIGN KEY (space_id) REFERENCES spaces(space_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='account space sessions';
CREATE TABLE `accspace_history` (
`accspace_history_id` bigint unsigned NOT NULL AUTO_INCREMENT,
`account_id` int unsigned NOT NULL,
`space_id` int unsigned NOT NULL,
`accspace_sessions_id` bigint unsigned NOT NULL,
`prompt` text NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`answer` text NOT NULL, -- JSON string, { "ja": "日本語", "en-US": "English" }...
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`accspace_history_id`),
KEY `accspace_history_IDX1` (`created`),
KEY `accspace_history_IDX2` (`deleted`),
KEY `accspace_history_IDX3` (`account_id`, `space_id`, `accspace_sessions_id`),
FOREIGN KEY (accspace_sessions_id) REFERENCES accspace_sessions(accspace_sessions_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id),
FOREIGN KEY (space_id) REFERENCES spaces(space_id)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='account space history';
CREATE TABLE `invite_user_sessions` (
`session_id` char(40) NOT NULL,
`sender_id` int unsigned NOT NULL,
`user_email` varchar(320) NOT NULL,
`role` tinyint DEFAULT 0, -- 0: Normal user, 1: Mgr
`space_id` int unsigned NOT NULL,
`created` DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`session_id`),
KEY `invite_user_sessions_IDX1` (`created`),
FOREIGN KEY (sender_id) REFERENCES accounts(account_id),
FOREIGN KEY (space_id) REFERENCES spaces(space_id)
) ENGINE=InnoDB DEFAULT CHARSET=ascii COMMENT='invite user sessions';
CREATE TABLE `space_users` (
`spcuser_id` int unsigned NOT NULL AUTO_INCREMENT,
`space_id` int unsigned NOT NULL,
`account_id` int unsigned NOT NULL,
`role` tinyint DEFAULT 0, -- 0: Normal user, 1: Mgr
`created` datetime DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
PRIMARY KEY (`spcuser_id`),
KEY `spcusers_IDX1` (`space_id`),
KEY `spcusers_IDX2` (`created`),
KEY `spcusers_IDX3` (`deleted`),
FOREIGN KEY (account_id) REFERENCES accounts(account_id),
FOREIGN KEY (space_id) REFERENCES spaces(space_id),
UNIQUE KEY `spcusers_IDX4` (`space_id`, `account_id`, `deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='space users';
-- init data
INSERT INTO `accounts` (`account_id`, `account_email`, `account_pw`) VALUES
(5, 'du@amipro.me', 'wuxiao1998');
INSERT INTO `space_templates` (`spacetemp_id`, `spacetemp_name`, `shared`, `account_id`, `spacetemp_intro`) VALUES
(5, '{"ja": "会社経営", "en-US": "Running Business"}', 1, 5, '{"ja": "小規模会社経営用スペース", "en-US": "Space for running small business"}');
INSERT INTO `spacetplt_info_blocks` (`info_block_id`, `spacetemp_id`, `block_name`) VALUES
(5, 5, '{"ja": "会社情報", "en-US": "Company Info"}'),
(6, 5, '{"ja": "従業員情報", "en-US": "Employee Info"}'),
(7, 5, '{"ja": "商品情報", "en-US": "Product Info"}'),
(8, 5, '{"ja": "顧客情報", "en-US": "Customer Info"}'),
(9, 5, '{"ja": "取引情報", "en-US": "Transaction Info"}');
update spacetplt_info_blocks set block_cmmt = '{"ja": "会社全体の情報、他人に説明する時のオーバービューの様な情報", "en-US": "Company-wide information, overview information for explaining to others"}' where info_block_id = 5;
INSERT INTO `spacetplt_prompts` (`prompt_id`, `spacetemp_id`, `prompt_head`, `prompt_hint`, `prompt_show`, `prompt_foot`) VALUES
(5, 5, '{"ja": "会社の経営者として", "en-US": "As a company executive"}', '{"ja": "質問の50%を適当の数字へ変更してください", "en-US": "Change 50% of the question to an appropriate number"}', '{"ja": "利益を50%増加できるために従業員が何人を増加雇用すべき?", "en-US": "How many more employees should be hired to increase profits by 50%?"}', '{"ja": "テーブルの形で以上の問題を回答して", "en-US": "Answer the above question in table form"}');
INSERT INTO `spacetplt_prompts` (`prompt_id`, `spacetemp_id`, `prompt_show`) VALUES
(6, 5, '{"ja": "セールス一人毎の平均売上は?", "en-US": "What is the average sale per salesperson?"}'),
(7, 5, '{"ja": "2024年月の売上は", "en-US": "Sales in May 2024"}'),
(8, 5, '{"ja": "毎月レストランの売上比率", "en-US": "Monthly restaurant sales ratio"}');

98
app.js Normal file
View File

@@ -0,0 +1,98 @@
const express = require('express');
const session = require('express-session');
require('dotenv').config();
const { env } = require("process");
const path = require('path');
const fs = require('fs');
const log4js = require('log4js');
log4js.configure('log4js-conf.json');
global.logger = log4js.getLogger();
const mysql = require('mysql2');
const { log } = require('console');
global.mysql_pool = mysql.createPool({
connectionLimit : process.env.MYSQL_POOL_LIMIT || 10,
host: process.env.MYSQL_HOST || 'localhost',
database: process.env.MYSQL_DATABASE || 'sisai_world_db',
user: process.env.MYSQL_USER || 'root',
password: process.env.MYSQL_PASSWD || '',
stringifyObjects: true,
});
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0; // Protected by Nginx.(for self-signed certificate. Comment out in product env.)
// Shutdown function to flush logs and clean up
const shutdown = () => {
logger.info('Shutting down sisai server...');
// Flush log4js logs
log4js.shutdown((error) => {
if (error) {
console.error('Error shutting down log4js:', error);
} else {
console.log('Log4js has been shut down.');
}
// Exit the process after logs have been flushed
process.exit();
});
};
// Handle various shutdown signals
['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK', 'exit'].forEach(signal => {
process.on(signal, () => shutdown());
});
global.app = express();
app.use(express.static('views'));
app.use(session({
secret: 'sisai_world_wEb_962287769',
resave: false,
saveUninitialized: true,
cookie: { secure: false, maxAge: 1000*60*60*24*300 } //300 days
}));
logger.info('Sisai World running on nodejs:' + process.version)
var server
if(process.env.SSLKEY && process.env.SSLCRT){
const options = {
key: fs.readFileSync(process.env.SSLKEY),
cert: fs.readFileSync(process.env.SSLCRT)
};
const https = require("https");
server = https.createServer(options, app)
}else{
const http = require("http");
server = http.createServer({}, app)
}
const port = process.env.PORT || 443;
server.listen(port, () => {
logger.info(`Started sisai world server: ${port}`);
});
//Map of session time
global.session_time = new Map();
const mainJS = './process.js';
function loadModule(modulePath) {
const resolvedPath = path.resolve(modulePath);
delete require.cache[require.resolve(resolvedPath)];
return require(modulePath);
}
global.processModule = loadModule(mainJS);
// Watch for changes in the main module
fs.watchFile(path.resolve(__dirname, mainJS), () => {
processModule = loadModule(mainJS);
logger.info('mainJS reloaded');
});

20
etc/Readme.txt Normal file
View File

@@ -0,0 +1,20 @@
* How to CICD to servers by git
** Create empty git repo on each server
mkdir -p ~/cicd/sisai-world-cicd.git
cd ~/cicd/sisai-world-cicd.git
git init --bare
** Copy svr_git_hooks_post-receive to ~/cicd/sisai-world-cicd.git/hooks/ on server
** Edit to fit server env and rename to post-receive
** chmod +x post-receive
** Edit and test code locally
** run deploy_XX.sh
**(Option)the first time may have to copy and edit .env file on servers
* Start node.js:
pm2 start app.js --name sisai

28
etc/deploy_37.sh Executable file
View File

@@ -0,0 +1,28 @@
# Define the path you want to deploy
DEPLOY_PATH="/Users/dqj/HDD/amiProProjects/amiprorepo/sisai-world"
# Create a temporary directory
TEMP_DIR=$(mktemp -d)
# Copy the specific path to the temporary directory
cp -r $DEPLOY_PATH/* $TEMP_DIR
# Navigate to the temporary directory
cd $TEMP_DIR
# Initialize a new Git repository
git init
# Add all files to the repository
git add .
# Commit the changes
git commit -m "Deploy sisai world"
# Add remote server repository
git remote add server ssh://centos@133.242.134.37/~/cicd/sisai-world-cicd.git
# Push the code to the remote server
git push --force server main
rm -rf $TEMP_DIR

30
etc/deploy_cluster.sh Executable file
View File

@@ -0,0 +1,30 @@
# Define the path you want to deploy
DEPLOY_PATH="/Users/dqj/HDD/amiProProjects/amiprorepo/sisai-world"
# Create a temporary directory
TEMP_DIR=$(mktemp -d)
# Copy the specific path to the temporary directory
cp -r $DEPLOY_PATH/* $TEMP_DIR
# Navigate to the temporary directory
cd $TEMP_DIR
# Initialize a new Git repository
git init
# Add all files to the repository
git add .
# Commit the changes
git commit -m "Deploy sisai world"
# Add remote server repository
git remote add server1 ssh://centos@153.127.28.185/~/cicd/sisai-world-cicd.git
git remote add server2 ssh://centos@133.242.149.212/~/cicd/sisai-world-cicd.git
# Push the code to the remote server
git push --force server1 main
git push --force server2 main
rm -rf $TEMP_DIR

388
etc/nginx.conf Normal file
View File

@@ -0,0 +1,388 @@
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
upstream sisai_nodejs {
ip_hash;
server 153.127.28.185:6066 weight=3;
server 133.242.149.212:6066 weight=2;
}
upstream nodejs {
ip_hash;
server 153.127.28.185:8060 weight=3;
server 133.242.149.212:8060 weight=2;
}
server {
server_name test.sisai.world;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
proxy_buffering off;
location / {
allow all;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_cookie_path ~*^/.* /;
proxy_pass http://localhost:8066/;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/sisai.world/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/sisai.world/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name sisai.world;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
proxy_buffering off;
location / {
allow all;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_cookie_path ~*^/.* /;
proxy_pass http://sisai_nodejs; # http://localhost:8060/;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/sisai.world/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/sisai.world/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name fido2.amipro.me;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location /.well-known {
alias /usr/share/nginx/html/sample-site/.well-known;
}
location / {
allow all;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_cookie_path ~*^/.* /;
proxy_pass http://nodejs; # http://localhost:8060/;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/fido2.amipro.me/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/fido2.amipro.me/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name sample.amipro.me;
root /usr/share/nginx/html/sample-site;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location / {
allow all;
index login.html;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/fido2.amipro.me/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/fido2.amipro.me/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name portal.amipro.me;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location / {
allow all;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Authorization, Accept";
add_header Access-Control-Allow-Credentials true;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_hide_header 'Access-Control-Allow-Origin';
proxy_cookie_path ~*^/.* /;
proxy_pass http://localhost:8065/;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/fido2.amipro.me/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/fido2.amipro.me/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name test.amipro.me;
root /usr/share/nginx/html/sample-site; #/usr/share/nginx/html; #/home/centos/test_sites;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location /.well-known {
alias /usr/share/nginx/html/sample-site/.well-known;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/fido2.amipro.me/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/fido2.amipro.me/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
# Settings for a TLS enabled server.
#
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers PROFILE=SYSTEM;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /404.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
server {
if ($host = fido2.amipro.me) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name fido2.amipro.me;
listen 80;
return 404; # managed by Certbot
}
server {
if ($host = test.amipro.me) {
return 301 https://$host$request_uri;
} # managed by Certbot
#if ($host = test.amipro.me) {
# return 301 https://$host$request_uri;
#} # managed by Certbot
server_name test.amipro.me;
listen 80;
#return 404; # managed by Certbot
}
server {
if ($host = portal.amipro.me) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name portal.amipro.me;
listen 80;
return 404; # managed by Certbot
}
server {
if ($host = sample.amipro.me) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name sample.amipro.me;
listen 80;
return 404; # managed by Certbot
}
server {
if ($host = sisai.world) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name sisai.world;
listen 80;
return 404; # managed by Certbot
}
server {
if ($host = sisai.world) {
return 301 https://$host$request_uri;
} # managed by Certbot
server_name sisai.world;
listen 80;
return 404; # managed by Certbot
}}

View File

@@ -0,0 +1,15 @@
# Git hooks in server's ~/your-app.git/hooks/post-receive
#!/bin/bash
TARGET_DIR=~/sisai-world
GIT_DIR=~/cicd/sisai-world-cicd.git
BRANCH=main
# Checkout the code to the target directory
git --work-tree=$TARGET_DIR --git-dir=$GIT_DIR checkout -f $BRANCH
# Navigate to the target directory
cd $TARGET_DIR
# Our code support hot reload, refer 'Hot reload support' in process.js

15
log4js-conf.json Normal file
View File

@@ -0,0 +1,15 @@
{
"appenders": {
"system":
{
"type": "file",
"filename": "logs/sisai_portal.log",
"maxLogSize": 10000000,
"backups": 100,
"layout": { "type": "pattern", "pattern": "%d[%p]%m" }
}
},
"categories": {
"default": { "appenders": ["system"], "level": "debug" }
}
}

78
logs/sisai_portal.log Normal file
View File

@@ -0,0 +1,78 @@
2024-07-10T22:23:16.403[INFO]Portal running on nodejs:v20.13.1
2024-07-10T22:23:16.407[INFO]Started portal server: 8065
2024-07-10T22:28:29.499[DEBUG]login: contact@witsign.com
2024-07-10T22:28:29.649[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:28:29.657[DEBUG]{ space_id: '18', conversation_id: '', language: 'ja' }
2024-07-10T22:28:33.710[DEBUG]{ role_type: 1 }
2024-07-10T22:29:15.073[DEBUG]{ type: 'public', page: 1, per_page: 12 }
2024-07-10T22:29:18.028[DEBUG]{ type: 'myown', page: 1, per_page: 12 }
2024-07-10T22:31:36.387[DEBUG]{ type: 'public', page: 1, per_page: 12 }
2024-07-10T22:32:19.525[DEBUG]{ role_type: 1 }
2024-07-10T22:33:20.628[DEBUG]{ email: 'dqj@witsign.com', role: '1', space_id: '18' }
2024-07-10T22:33:22.884[INFO]Email sent: 250 OK id=1sRXRi-002gd8-1u; email=dqj@witsign.com; subject=SisAi Worldサービス招待; message=undefined
2024-07-10T22:33:59.812[DEBUG]login: dqj@witsign.com
2024-07-10T22:34:00.288[DEBUG]{ space_id: '17', language: 'ja' }
2024-07-10T22:34:00.317[DEBUG]{ space_id: '17', conversation_id: '', language: 'ja' }
2024-07-10T22:34:29.462[DEBUG]{ session: '017a21fd-46f3-4e47-8cb0-f05d4b778b8c' }
2024-07-10T22:34:32.470[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:34:32.502[DEBUG]{ space_id: '18', conversation_id: '', language: 'ja' }
2024-07-10T22:34:45.746[DEBUG]{ role_type: 1 }
2024-07-10T22:34:57.673[DEBUG]{ space_id: 18, user_id: 20 }
2024-07-10T22:35:16.157[DEBUG]{ all_user: false }
2024-07-10T22:35:16.833[DEBUG]{ role_type: 1 }
2024-07-10T22:35:49.959[DEBUG]login: contact@witsign.com
2024-07-10T22:35:50.025[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:35:50.031[DEBUG]{ space_id: '18', conversation_id: '', language: 'ja' }
2024-07-10T22:35:51.585[DEBUG]{ role_type: 1 }
2024-07-10T22:36:17.317[DEBUG]{ email: 'dqj@witsign.com', role: '0', space_id: '18' }
2024-07-10T22:36:19.659[INFO]Email sent: 250 OK id=1sRXUZ-002jX9-1A; email=dqj@witsign.com; subject=SisAi Worldサービス招待; message=undefined
2024-07-10T22:36:46.036[DEBUG]{ session: 'e3f1e22d-3b4f-43ae-8d31-ed39ae885530' }
2024-07-10T22:36:57.410[DEBUG]login: dqj@witsign.com
2024-07-10T22:36:57.806[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:36:57.818[DEBUG]{ space_id: '18', conversation_id: '', language: 'ja' }
2024-07-10T22:37:08.639[DEBUG]{ role_type: 1 }
2024-07-10T22:38:13.655[DEBUG]{ all_user: false }
2024-07-10T22:38:17.987[DEBUG]{ type: 'public', page: 1, per_page: 12 }
2024-07-10T22:38:20.282[DEBUG]{ type: 'myown', page: 1, per_page: 12 }
2024-07-10T22:38:22.737[DEBUG]{ type: 'public', page: 1, per_page: 12 }
2024-07-10T22:38:30.824[DEBUG]{ role_type: 1 }
2024-07-10T22:38:39.096[DEBUG]login: contact@witsign.com
2024-07-10T22:38:39.175[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:38:40.853[DEBUG]{ role_type: 1 }
2024-07-10T22:38:46.160[DEBUG]{ space_id: 18, user_id: 20, role: '1' }
2024-07-10T22:39:02.507[DEBUG]login: dqj@witsign.com
2024-07-10T22:39:02.898[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:39:05.267[DEBUG]{ role_type: 1 }
2024-07-10T22:43:55.653[INFO]Portal running on nodejs:v20.13.1
2024-07-10T22:43:55.657[INFO]Started portal server: 8065
2024-07-10T22:44:03.620[DEBUG]login: contact@witsign.com
2024-07-10T22:44:03.745[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:44:03.774[DEBUG]{ space_id: '18', conversation_id: '', language: 'ja' }
2024-07-10T22:44:05.531[DEBUG]{ role_type: 1 }
2024-07-10T22:45:03.519[DEBUG]{ email: 'dqj1998@gmail.com', role: '0', space_id: '18' }
2024-07-10T22:45:06.072[INFO]Email sent: 250 OK id=1sRXd3-002uVI-2V; email=dqj1998@gmail.com; subject=SisAi Worldサービス招待; message=undefined
2024-07-10T22:45:39.523[DEBUG]{ session: '1b74a5a1-6d21-460e-80ad-b0d8256bd16e' }
2024-07-10T22:45:45.050[DEBUG]login: contact@witsign.com
2024-07-10T22:45:45.198[DEBUG]{ space_id: '18', language: 'ja' }
2024-07-10T22:45:45.206[DEBUG]{ space_id: '18', conversation_id: '', language: 'ja' }
2024-07-10T22:45:47.347[DEBUG]{ role_type: 1 }
2024-07-10T22:46:05.463[DEBUG]{ space_id: 18, user_id: 7, role: '1' }
2024-07-10T22:46:07.499[DEBUG]{ space_id: 18, user_id: 7, role: '0' }
2024-07-10T22:46:45.551[DEBUG]{ all_user: false }
2024-07-10T22:46:45.560[DEBUG]{ tmpl_id: '18', language: 'ja' }
2024-07-10T22:46:45.568[DEBUG]{ tmpl_id: '18', language: 'ja' }
2024-07-10T22:46:46.526[DEBUG]{ role_type: 1 }
2024-07-10T22:48:58.772[DEBUG]{ role_type: 1 }
2024-07-10T22:49:07.267[DEBUG]{ role_type: 1 }
2024-07-10T22:49:19.534[DEBUG]{ space_id: 18, user_id: 7, role: '1' }
2024-07-10T22:49:20.797[DEBUG]{ type: 'public', page: 1, per_page: 12 }
2024-07-10T22:49:21.896[DEBUG]{ role_type: 1 }
2024-07-10T22:49:29.782[DEBUG]{ space_id: 18, user_id: 7 }
2024-07-10T22:49:35.867[DEBUG]{ space_id: 18, user_id: 17 }
2024-07-10T22:49:35.986[DEBUG]{ role_type: 1 }
2024-07-10T22:57:02.812[INFO]Portal running on nodejs:v20.13.1
2024-07-10T22:57:02.814[INFO]Started portal server: 8065
2024-07-10T22:57:10.187[DEBUG]login: contact@witsign.com
2024-07-10T22:57:10.303[DEBUG]{ space_id: '17', language: 'ja' }
2024-07-10T22:57:10.311[DEBUG]{ space_id: '17', conversation_id: '', language: 'ja' }
2024-07-10T22:57:12.196[DEBUG]{ role_type: 1 }

1093
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

12
package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"dependencies": {
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-session": "^1.17.3",
"log4js": "^6.9.1",
"mysql2": "^3.2.3",
"nodemailer": "^6.9.1",
"openai": "^4.52.0",
"uuid": "^9.0.0"
}
}

2935
process.js Normal file

File diff suppressed because it is too large Load Diff

1254
views/builder.html Normal file

File diff suppressed because it is too large Load Diff

404
views/docs.html Normal file
View File

@@ -0,0 +1,404 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>SisAi World</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<script src="files/jquery.js"></script>
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/ua-parser.js"></script>
<script src="files/amipro_utils.js?v=20230501"></script>
<script src="files/portal.js"></script>
<script>
lang_map = new Map();
lang_map.set("en-US", "About SisAi World");
lang_map.set("zh-CN", "关于SisAi World");
lang_map.set("ja", "SisAi Worldについて");
i18n_messages.set("btn_doc1", lang_map);
lang_map = new Map();
lang_map.set("en-US", "SisAiWorld is a general software platform based on AI models, and users can effectively utilize various software functions and services by using templates provided by AI experts.<br><br>* <b>Software is AI</b>: Let AI directly provide functions to users, no longer need forever unsatisfactory software development.<br>* <b>Value of AI experts</b>: Free sharing cannot create a healthy business environment, we protect the intellectual property rights of experts.<br>* <b>Power of collaboration</b>: The collaboration of experts and users opens the door to the future of AI.");
lang_map.set("zh-CN", "SisAiWorld是一个基于人工智能模型的通用软件平台用户可以通过使用人工智能专家提供的模板有效利用人工智能模型获得各种软件功能与服务。<br><br>* <b>软件就是AI</b>: 让AI直接为用户提供功能不再需要永远不尽人意的软件开发。<br>* <b>AI专家的价值</b>: 免费的分享不能产生健康的商业环境,我们保护专家的知识产权。<br>* <b>协作的力量</b>:专家及用户的协作为开拓AI的未来打开大门。");
lang_map.set("ja", "SisAiWorld は、AI モデルに基づいた汎用ソフトウェアプラットフォームであり、AI エキスパートが提供するテンプレートを使用することで、ユーザーはさまざまなソフトウェア機能とサービスを効果的に活用できます。<br><br>* <b>ソフトウェアはAI</b>: AI がユーザーに直接機能を提供し、永遠に不十分なソフトウェア開発は不要です。<br>* <b>AI エキスパートの価値</b>: 無料の共有は健全なビジネス環境を生み出すことができません。私たちは専門家の知的財産権を保護します。<br>* <b>協力の力</b>: 専門家とユーザーの協力は、AI の未来を切り開く大きな扉を開きます。");
i18n_messages.set("info_doc1", lang_map);
lang_map = new Map();
lang_map.set("en-US", "General user usage method");
lang_map.set("zh-CN", "一般用户使用方法");
lang_map.set("ja", "一般ユーザーの使用方法");
i18n_messages.set("btn_doc2", lang_map);
lang_map = new Map();
lang_map.set("en-US", "1. Visit the SisAiWorld homepage <a href='https://SisAi.world'>https://SisAi.world</a> and register an account for free using an email address.<br>2. Create the selected space from the [Space Market] page.<br>3. Enter background information on the [Space Settings] page.<br>4. Click on the recommended question or enter a question to start using on the [Conversation] page!");
lang_map.set("zh-CN", "1、访问SisAiWorld首页 <a href='https://SisAi.world'>https://SisAi.world</a>,使用邮箱地址免费注册账户。<br>2、从【空间市场】页面中创建选定的空间。<br>3、在【空间设定】页面中输入背景信息。<br>4、在【会话】页面点击推荐问题或输入问题开始使用");
lang_map.set("ja", "1. SisAi World のホームページ <a href='https://SisAi.world'>https://SisAi.world</a>にアクセスし、メールアドレスで無料アカウントを登録します。<br>2. [スペースマーケット] ページから選択したスペースを作成します。<br>3. [スペース設定] ページに背景情報を入力します。<br>4. [会話] ページで推奨質問をクリックするか、質問を入力して使用を開始します!");
i18n_messages.set("info_doc2", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Create your own template");
lang_map.set("zh-CN", "创建自己的空间模板");
lang_map.set("ja", "自分のテンプレートを作成");
i18n_messages.set("btn_doc3", lang_map);
lang_map = new Map();
lang_map.set("en-US", "If you are an AI expert or interested in sharing your ideas, you can create and share space templates.<br><br>1. Log in to the SisAiWorld account.<br>2. Go to the [Space builder] page.<br>3. Enter the template name and description and [Save].<br>4. Choose whether to make this template public to the template market. You can also invite specific users without making it public.<br>5. Add background information types and [Save] each time you add one. Users can enter background information in advance without having to enter it each time they ask. Keep the classification as concise as possible, too many classifications will reduce the user experience.<br>6. Add recommended questions and [Save] each time you add one. The prompt head and foot are hidden from the user to <b>protect your intellectual property</b>!");
lang_map.set("zh-CN", "如果您是人工智能专家或有兴趣分享您的创意,可以创建并分享空间模板。<br><br>1, 登录进入SisAiWorld账户。<br>2,进入【空间构建】页面。<br>3,输入模板名称及描述并【保存】。<br>4,选择是否公开此模板到模板市场。不公开也可以通过邀请与特定用户分享。<br>5,添加背景信息类型,并每添加一项即【保存】。用户可以预先输入背景信息,而无需每次提问时重复输入。分类尽量简洁,过多分类会降低用户体验。<br>6,添加推荐提问,并每添加一项即【保存】。提问信息前缀和后缀对用户是隐藏的,<b>以保护您的知识产权!</b>");
lang_map.set("ja", "人工知能エキスパートであるか、自分のアイデアを共有したい場合は、スペーステンプレートを作成して共有できます。<br><br>1、SisAi World アカウントにログインします。<br>2、[スペースビルダー] ページに移動します。<br>3、テンプレート名と説明を入力して [保存] します。<br>4、このテンプレートをテンプレートマーケットに公開するかどうかを選択します。公開しなくても、特定のユーザーを招待できます。<br>5、背景情報カテゴリを追加し、1 つ追加するたびに [保存] します。ユーザーは事前に背景情報を入力できるため、毎回質問する時に繰り返し入力必要はありません。カテゴリをできるだけ簡潔にし、カテゴリが多すぎるとユーザーの体験が低下します。<br>6、推奨質問を追加し、1 つ追加するたびに [保存] します。質問のヘッドとフットはユーザーには非表示で、<b>貴方の知的財産権を保護します。</b>");
i18n_messages.set("info_doc3", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Invite users");
lang_map.set("zh-CN", "邀请用户协同工作");
lang_map.set("ja", "ユーザーを招待");
i18n_messages.set("btn_doc4", lang_map);
lang_map = new Map();
lang_map.set("en-US", "You can invite users to join your space to gain users or to collaborate.<br><br>1. Create the space you want to share in the [Space Market].<br>2. Go to the [Users] page.<br>3. Enter the user's email, select the space, and select the user's permissions (administrators can invite other users or delete users(including your account) from the space).<br>4. Click [Invite User].<br>5. The system will send an invitation email to the user.<br>6. Users who already have a SisAiWorld account can log in and start using the space, and users who do not yet have an account will be guided to the registration page to register an account first.");
lang_map.set("zh-CN", "可以通过邮件邀请用户加入到您的空间,获得用户或进行协同作业。<br><br>1. 在【空间市场】中创建准备分享的空间。<br>2进入【用户】页面。<br>3输入用户邮箱选择空间选择用户权限管理员可以邀请其他用户或从空间删除用户包括删除您的账号<br>4点击【邀请用户】<br>5系统将发送邀请邮件给用户。<br>6已经拥有SisAiWorld账号的用户可以登入系统并开始使用空间尚无账号的用户会被引导至注册页面首先注册账号。");
lang_map.set("ja", "ユーザーを招待して、スペースに参加してもらい、ユーザーを取得か、協力してもらうことができます。<br><br>1. [スペースマーケット] で共有したいスペースを作成します。<br>2. [ユーザー] ページに移動します。<br>3. ユーザーのメールアドレスを入力し、スペースを選択し、ユーザーの権限を選択します(管理者は他のユーザーを招待したり、ユーザー(あなたのアカウントも含め)をスペースから削除することができます)。<br>4. [ユーザーを招待] をクリックします。<br>5. システムはユーザーに招待メールを送信します。<br>6. すでに SisAiWorld アカウントを持っているユーザーはログインしてスペースを使用でき、まだアカウントを持っていないユーザーは、まずアカウントを登録するための登録ページに案内されます。");
i18n_messages.set("info_doc4", lang_map);
lang_map = new Map();
lang_map.set("en-US", "About SisAi World");
lang_map.set("zh-CN", "关于SisAi World");
lang_map.set("ja", "SisAi Worldについて");
i18n_messages.set("h_title_about", lang_map);
$(async function(){
//await set_i18n_messages();
if(navigator.language.toLocaleLowerCase().indexOf("ja")>=0){
document.getElementById("sisai_about_img").src = "files/sisai_about_ja.jpg";
}else if(navigator.language.toLocaleLowerCase().indexOf("cn")>=0){
document.getElementById("sisai_about_img").src = "files/sisai_about_cn.jpg";
}else document.getElementById("sisai_about_img").src = "files/sisai_about_en.jpg";
});
function clickedSisAiAbout(){
if(navigator.language.toLocaleLowerCase().indexOf("ja")>=0){
window.open("files/SisAi-About-ja.pdf", "_blank");
}else if(navigator.language.toLocaleLowerCase().indexOf("cn")>=0){
window.open("files/SisAi-About-cn.pdf", "_blank");
}else window.open("files/SisAi-About-en.pdf", "_blank");
}
</script>
</head>
<body>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="top.html" class="app-brand-link">
<span class="app-brand-logo demo">
<img src="files/favicon.ico" alt="SisAi" width="45%" style="display: block; margin: 10%;" />
</span>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<li class="menu-item">
<a href="top.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics" id="menu_conversation">Conversation</div>
</a>
</li>
<li class="menu-item">
<a href="settings.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div data-i18n="Analytics" id="menu_settings">Settings</div>
</a>
</li>
<li class="menu-item">
<a href="market.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-store-alt"></i>
<div data-i18n="Analytics" id="menu_market">market</div>
</a>
</li>
<li class="menu-item">
<a href="builder.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-wrench"></i>
<div data-i18n="Analytics" id="menu_builder">Space Builder</div>
</a>
</li>
<li class="menu-item">
<a href="mgr.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-user"></i>
<div data-i18n="Analytics" id="menu_mgr">Managers</div>
</a>
</li>
<li class="menu-item active">
<a href="docs.html" class="menu-link" onclick="javascript:return false;">
<i class="menu-icon tf-icons bx bx-file"></i>
<div data-i18n="Analytics" id="menu_docs">Documents</div>
</a>
</li>
</ul>
</aside>
<!-- / Menu -->
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
SisAi World
</div>
</div>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="javascript:void(0);" data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block" id="user_id">A manager</span>
<!--small class="text-muted">Admin<small-->
</div>
</div>
</a>
</li>
<!-- li>
<div class="dropdown-divider"></div>
</>
<li>
<a class="dropdown-item" href="devices.html">
<i class="bx bx-mobile-alt me-2"></i>
<span class="align-middle" id="label_device_mng">Device management</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<span class="d-flex align-items-center align-middle">
<i class="flex-shrink-0 bx bx-credit-card me-2"></i>
<span class="flex-grow-1 align-middle">Billing</span>
<span class="flex-shrink-0 badge badge-center rounded-pill bg-danger w-px-20 h-px-20">!</span>
</span>
</a>
</li -->
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="login.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle" id="label_logout">Log Out</span>
</a>
</li>
</ul>
</li>
<!--/ User -->
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
<div class="accordion mt-3">
<div class="card accordion-item">
<h2 class="accordion-header" id="headingOne">
<button id="btn_doc1" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordionOne" aria-expanded="false" aria-controls="accordionOne">
About
</button>
</h2>
<div id="accordionOne" class="accordion-collapse collapse">
<div class="accordion-body" id="info_doc1">
Recovery Guide
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading2">
<button id="btn_doc2" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion2" aria-expanded="false" aria-controls="accordion2">
Use Guide
</button>
</h2>
<div id="accordion2" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc2">
Use Guide
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading3">
<button id="btn_doc3" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion3" aria-expanded="false" aria-controls="accordion3">
Builder
</button>
</h2>
<div id="accordion3" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc3">
Builder
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading4">
<button id="btn_doc4" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion4" aria-expanded="false" aria-controls="accordion4">
invitation users
</button>
</h2>
<div id="accordion4" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc4">
invitation users
</div>
</div>
</div>
</div>
<br>
<div class="row" >
<!-- templates list -->
<div class="col-lg-6 col-md-6 col-12 mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title" id="h_title_about">About SisAi</h5>
<img src="files/sisai_about_en.jpg" alt="sisai_about" class="img-fluid" id="sisai_about_img" onclick="javascript:clickedSisAiAbout();"/>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered by OpenAI-4o)</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- Core JS -->
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<!-- endbuild -->
<!-- Page JS -->
<script src="files/dashboards-analytics.js"></script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

BIN
views/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,57 @@
/**
* amiPro utils
*/
'use strict';
function setI18NText(i18n_map){
for (const key of i18n_map.keys()) {
const elm = $("#"+key);
if(elm){
const lang = window.navigator.language;
var elem = i18n_map.get(key)
var msg = null
if(elem){
msg = elem.get(lang)
if(!msg)msg=elem.get('en-US');
}
if(!msg)msg = key+"-"+lang
$("#"+key).html(msg);
}
}
}
function getI18NText(i18n_map, key){
const lang = window.navigator.language;
var elem = i18n_map.get(key)
var msg = null
if(elem){
msg = elem.get(lang)
if(!msg)msg=elem.get('en-US');
}
if(!msg)msg = key+"-"+lang
return msg
}
function getI18NJsonText(json_text){
const lang = window.navigator.language;
var msg = null
try {
var parsedJson = (typeof json_text == "object")?json_text:JSON.parse(json_text);
msg = parsedJson[lang] || parsedJson['en-US'];
if(!msg && 0<Object.keys(parsedJson).length)msg = parsedJson[Object.keys(parsedJson)[0]];
//TODO: Translate to lang by AI call
} catch (e) {
msg=json_text;
}
return msg
}
function buildI18NJson(text, lang=window.navigator.language) {
//const lang = window.navigator.language;
var json = {};
json[lang] = text;
return JSON.stringify(json);
}

BIN
views/files/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

809
views/files/bootstrap.js vendored Normal file

File diff suppressed because one or more lines are too long

6511
views/files/boxicons.css Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

27
views/files/config.js Normal file
View File

@@ -0,0 +1,27 @@
/**
* Config
* -------------------------------------------------------------------------------------
* ! IMPORTANT: Make sure you clear the browser local storage In order to see the config changes in the template.
* ! To clear local storage: (https://www.leadshook.com/help/how-to-clear-local-storage-in-google-chrome-browser/).
*/
'use strict';
// JS global variables
let config = {
colors: {
primary: '#696cff',
secondary: '#8592a3',
success: '#71dd37',
info: '#03c3ec',
warning: '#ffab00',
danger: '#ff3e1d',
dark: '#233446',
black: '#000',
white: '#fff',
body: '#f4f5fb',
headingColor: '#566a7f',
axisColor: '#a1acb8',
borderColor: '#eceef1'
}
};

16643
views/files/core.css Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,662 @@
/**
* Dashboard Analytics
*/
'use strict';
(function () {
let cardColor, headingColor, axisColor, shadeColor, borderColor;
cardColor = config.colors.white;
headingColor = config.colors.headingColor;
axisColor = config.colors.axisColor;
borderColor = config.colors.borderColor;
// Total Revenue Report Chart - Bar Chart
// --------------------------------------------------------------------
const totalRevenueChartEl = document.querySelector('#totalRevenueChart'),
totalRevenueChartOptions = {
series: [
{
name: '2021',
data: [18, 7, 15, 29, 18, 12, 9]
},
{
name: '2020',
data: [-13, -18, -9, -14, -5, -17, -15]
}
],
chart: {
height: 300,
stacked: true,
type: 'bar',
toolbar: { show: false }
},
plotOptions: {
bar: {
horizontal: false,
columnWidth: '33%',
borderRadius: 12,
startingShape: 'rounded',
endingShape: 'rounded'
}
},
colors: [config.colors.primary, config.colors.info],
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 6,
lineCap: 'round',
colors: [cardColor]
},
legend: {
show: true,
horizontalAlign: 'left',
position: 'top',
markers: {
height: 8,
width: 8,
radius: 12,
offsetX: -3
},
labels: {
colors: axisColor
},
itemMargin: {
horizontal: 10
}
},
grid: {
borderColor: borderColor,
padding: {
top: 0,
bottom: -8,
left: 20,
right: 20
}
},
xaxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
labels: {
style: {
fontSize: '13px',
colors: axisColor
}
},
axisTicks: {
show: false
},
axisBorder: {
show: false
}
},
yaxis: {
labels: {
style: {
fontSize: '13px',
colors: axisColor
}
}
},
responsive: [
{
breakpoint: 1700,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '32%'
}
}
}
},
{
breakpoint: 1580,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '35%'
}
}
}
},
{
breakpoint: 1440,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '42%'
}
}
}
},
{
breakpoint: 1300,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '48%'
}
}
}
},
{
breakpoint: 1200,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '40%'
}
}
}
},
{
breakpoint: 1040,
options: {
plotOptions: {
bar: {
borderRadius: 11,
columnWidth: '48%'
}
}
}
},
{
breakpoint: 991,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '30%'
}
}
}
},
{
breakpoint: 840,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '35%'
}
}
}
},
{
breakpoint: 768,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '28%'
}
}
}
},
{
breakpoint: 640,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '32%'
}
}
}
},
{
breakpoint: 576,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '37%'
}
}
}
},
{
breakpoint: 480,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '45%'
}
}
}
},
{
breakpoint: 420,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '52%'
}
}
}
},
{
breakpoint: 380,
options: {
plotOptions: {
bar: {
borderRadius: 10,
columnWidth: '60%'
}
}
}
}
],
states: {
hover: {
filter: {
type: 'none'
}
},
active: {
filter: {
type: 'none'
}
}
}
};
if (typeof totalRevenueChartEl !== undefined && totalRevenueChartEl !== null) {
const totalRevenueChart = new ApexCharts(totalRevenueChartEl, totalRevenueChartOptions);
totalRevenueChart.render();
}
// Growth Chart - Radial Bar Chart
// --------------------------------------------------------------------
const growthChartEl = document.querySelector('#growthChart'),
growthChartOptions = {
series: [78],
labels: ['Growth'],
chart: {
height: 240,
type: 'radialBar'
},
plotOptions: {
radialBar: {
size: 150,
offsetY: 10,
startAngle: -150,
endAngle: 150,
hollow: {
size: '55%'
},
track: {
background: cardColor,
strokeWidth: '100%'
},
dataLabels: {
name: {
offsetY: 15,
color: headingColor,
fontSize: '15px',
fontWeight: '600',
fontFamily: 'Public Sans'
},
value: {
offsetY: -25,
color: headingColor,
fontSize: '22px',
fontWeight: '500',
fontFamily: 'Public Sans'
}
}
}
},
colors: [config.colors.primary],
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
shadeIntensity: 0.5,
gradientToColors: [config.colors.primary],
inverseColors: true,
opacityFrom: 1,
opacityTo: 0.6,
stops: [30, 70, 100]
}
},
stroke: {
dashArray: 5
},
grid: {
padding: {
top: -35,
bottom: -10
}
},
states: {
hover: {
filter: {
type: 'none'
}
},
active: {
filter: {
type: 'none'
}
}
}
};
if (typeof growthChartEl !== undefined && growthChartEl !== null) {
const growthChart = new ApexCharts(growthChartEl, growthChartOptions);
growthChart.render();
}
// Profit Report Line Chart
// --------------------------------------------------------------------
const profileReportChartEl = document.querySelector('#profileReportChart'),
profileReportChartConfig = {
chart: {
height: 80,
// width: 175,
type: 'line',
toolbar: {
show: false
},
dropShadow: {
enabled: true,
top: 10,
left: 5,
blur: 3,
color: config.colors.warning,
opacity: 0.15
},
sparkline: {
enabled: true
}
},
grid: {
show: false,
padding: {
right: 8
}
},
colors: [config.colors.warning],
dataLabels: {
enabled: false
},
stroke: {
width: 5,
curve: 'smooth'
},
series: [
{
data: [110, 270, 145, 245, 205, 285]
}
],
xaxis: {
show: false,
lines: {
show: false
},
labels: {
show: false
},
axisBorder: {
show: false
}
},
yaxis: {
show: false
}
};
if (typeof profileReportChartEl !== undefined && profileReportChartEl !== null) {
const profileReportChart = new ApexCharts(profileReportChartEl, profileReportChartConfig);
profileReportChart.render();
}
// Order Statistics Chart
// --------------------------------------------------------------------
const chartOrderStatistics = document.querySelector('#orderStatisticsChart'),
orderChartConfig = {
chart: {
height: 165,
width: 130,
type: 'donut'
},
labels: ['Electronic', 'Sports', 'Decor', 'Fashion'],
series: [85, 15, 50, 50],
colors: [config.colors.primary, config.colors.secondary, config.colors.info, config.colors.success],
stroke: {
width: 5,
colors: cardColor
},
dataLabels: {
enabled: false,
formatter: function (val, opt) {
return parseInt(val) + '%';
}
},
legend: {
show: false
},
grid: {
padding: {
top: 0,
bottom: 0,
right: 15
}
},
plotOptions: {
pie: {
donut: {
size: '75%',
labels: {
show: true,
value: {
fontSize: '1.5rem',
fontFamily: 'Public Sans',
color: headingColor,
offsetY: -15,
formatter: function (val) {
return parseInt(val) + '%';
}
},
name: {
offsetY: 20,
fontFamily: 'Public Sans'
},
total: {
show: true,
fontSize: '0.8125rem',
color: axisColor,
label: 'Weekly',
formatter: function (w) {
return '38%';
}
}
}
}
}
}
};
if (typeof chartOrderStatistics !== undefined && chartOrderStatistics !== null) {
const statisticsChart = new ApexCharts(chartOrderStatistics, orderChartConfig);
statisticsChart.render();
}
// Income Chart - Area chart
// --------------------------------------------------------------------
const incomeChartEl = document.querySelector('#incomeChart'),
incomeChartConfig = {
series: [
{
data: [24, 21, 30, 22, 42, 26, 35, 29]
}
],
chart: {
height: 215,
parentHeightOffset: 0,
parentWidthOffset: 0,
toolbar: {
show: false
},
type: 'area'
},
dataLabels: {
enabled: false
},
stroke: {
width: 2,
curve: 'smooth'
},
legend: {
show: false
},
markers: {
size: 6,
colors: 'transparent',
strokeColors: 'transparent',
strokeWidth: 4,
discrete: [
{
fillColor: config.colors.white,
seriesIndex: 0,
dataPointIndex: 7,
strokeColor: config.colors.primary,
strokeWidth: 2,
size: 6,
radius: 8
}
],
hover: {
size: 7
}
},
colors: [config.colors.primary],
fill: {
type: 'gradient',
gradient: {
shade: shadeColor,
shadeIntensity: 0.6,
opacityFrom: 0.5,
opacityTo: 0.25,
stops: [0, 95, 100]
}
},
grid: {
borderColor: borderColor,
strokeDashArray: 3,
padding: {
top: -20,
bottom: -8,
left: -10,
right: 8
}
},
xaxis: {
categories: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
axisBorder: {
show: false
},
axisTicks: {
show: false
},
labels: {
show: true,
style: {
fontSize: '13px',
colors: axisColor
}
}
},
yaxis: {
labels: {
show: false
},
min: 10,
max: 50,
tickAmount: 4
}
};
if (typeof incomeChartEl !== undefined && incomeChartEl !== null) {
const incomeChart = new ApexCharts(incomeChartEl, incomeChartConfig);
incomeChart.render();
}
// Expenses Mini Chart - Radial Chart
// --------------------------------------------------------------------
const weeklyExpensesEl = document.querySelector('#expensesOfWeek'),
weeklyExpensesConfig = {
series: [65],
chart: {
width: 60,
height: 60,
type: 'radialBar'
},
plotOptions: {
radialBar: {
startAngle: 0,
endAngle: 360,
strokeWidth: '8',
hollow: {
margin: 2,
size: '45%'
},
track: {
strokeWidth: '50%',
background: borderColor
},
dataLabels: {
show: true,
name: {
show: false
},
value: {
formatter: function (val) {
return '$' + parseInt(val);
},
offsetY: 5,
color: '#697a8d',
fontSize: '13px',
show: true
}
}
}
},
fill: {
type: 'solid',
colors: config.colors.primary
},
stroke: {
lineCap: 'round'
},
grid: {
padding: {
top: -10,
bottom: -15,
left: -10,
right: -10
}
},
states: {
hover: {
filter: {
type: 'none'
}
},
active: {
filter: {
type: 'none'
}
}
}
};
if (typeof weeklyExpensesEl !== undefined && weeklyExpensesEl !== null) {
const weeklyExpenses = new ApexCharts(weeklyExpensesEl, weeklyExpensesConfig);
weeklyExpenses.render();
}
})();

107
views/files/demo.css Normal file
View File

@@ -0,0 +1,107 @@
/*
* demo.css
* File include item demo only specific css only
******************************************************************************/
.menu .app-brand.demo {
height: 64px;
margin-top: 12px;
}
.app-brand-logo.demo svg {
width: 22px;
height: 38px;
}
.app-brand-text.demo {
font-size: 1.75rem;
letter-spacing: -0.5px;
text-transform: lowercase;
}
/* ! For .layout-navbar-fixed added fix padding top tpo .layout-page */
/* Detached navbar */
.layout-navbar-fixed .layout-wrapper:not(.layout-horizontal):not(.layout-without-menu) .layout-page {
padding-top: 76px !important;
}
/* Default navbar */
.layout-navbar-fixed .layout-wrapper:not(.layout-without-menu) .layout-page {
padding-top: 64px !important;
}
/* Navbar page z-index issue solution */
.content-wrapper .navbar {
z-index: auto;
}
/*
* Content
******************************************************************************/
.demo-blocks > * {
display: block !important;
}
.demo-inline-spacing > * {
margin: 1rem 0.375rem 0 0 !important;
}
/* ? .demo-vertical-spacing class is used to have vertical margins between elements. To remove margin-top from the first-child, use .demo-only-element class with .demo-vertical-spacing class. For example, we have used this class in forms-input-groups.html file. */
.demo-vertical-spacing > * {
margin-top: 1rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing.demo-only-element > :first-child {
margin-top: 0 !important;
}
.demo-vertical-spacing-lg > * {
margin-top: 1.875rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing-lg.demo-only-element > :first-child {
margin-top: 0 !important;
}
.demo-vertical-spacing-xl > * {
margin-top: 5rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing-xl.demo-only-element > :first-child {
margin-top: 0 !important;
}
.rtl-only {
display: none !important;
text-align: left !important;
direction: ltr !important;
}
[dir='rtl'] .rtl-only {
display: block !important;
}
/*
* Layout demo
******************************************************************************/
.layout-demo-wrapper {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
margin-top: 1rem;
}
.layout-demo-placeholder img {
width: 900px;
}
.layout-demo-info {
text-align: center;
margin-top: 1rem;
}

BIN
views/files/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

101
views/files/helpers.js Normal file

File diff suppressed because one or more lines are too long

112
views/files/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

118
views/files/main.js Normal file
View File

@@ -0,0 +1,118 @@
/**
* Main
*/
'use strict';
let menu, animate;
(function () {
// Initialize menu
//-----------------
let layoutMenuEl = document.querySelectorAll('#layout-menu');
layoutMenuEl.forEach(function (element) {
menu = new Menu(element, {
orientation: 'vertical',
closeChildren: false
});
// Change parameter to true if you want scroll animation
window.Helpers.scrollToActive((animate = false));
window.Helpers.mainMenu = menu;
});
// Initialize menu togglers and bind click on each
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
menuToggler.forEach(item => {
item.addEventListener('click', event => {
event.preventDefault();
window.Helpers.toggleCollapsed();
});
});
// Display menu toggle (layout-menu-toggle) on hover with delay
let delay = function (elem, callback) {
let timeout = null;
elem.onmouseenter = function () {
// Set timeout to be a timer which will invoke callback after 300ms (not for small screen)
if (!Helpers.isSmallScreen()) {
timeout = setTimeout(callback, 300);
} else {
timeout = setTimeout(callback, 0);
}
};
elem.onmouseleave = function () {
// Clear any timers set to timeout
document.querySelector('.layout-menu-toggle').classList.remove('d-block');
clearTimeout(timeout);
};
};
if (document.getElementById('layout-menu')) {
delay(document.getElementById('layout-menu'), function () {
// not for small screen
if (!Helpers.isSmallScreen()) {
document.querySelector('.layout-menu-toggle').classList.add('d-block');
}
});
}
// Display in main menu when menu scrolls
let menuInnerContainer = document.getElementsByClassName('menu-inner'),
menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0];
if (menuInnerContainer.length > 0 && menuInnerShadow) {
menuInnerContainer[0].addEventListener('ps-scroll-y', function () {
if (this.querySelector('.ps__thumb-y').offsetTop) {
menuInnerShadow.style.display = 'block';
} else {
menuInnerShadow.style.display = 'none';
}
});
}
// Init helpers & misc
// --------------------
// Init BS Tooltip
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Accordion active class
const accordionActiveFunction = function (e) {
if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') {
e.target.closest('.accordion-item').classList.add('active');
} else {
e.target.closest('.accordion-item').classList.remove('active');
}
};
const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion'));
const accordionList = accordionTriggerList.map(function (accordionTriggerEl) {
accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction);
accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction);
});
// Auto update layout based on screen size
window.Helpers.setAutoUpdate(true);
// Toggle Password Visibility
window.Helpers.initPasswordToggle();
// Speech To Text
window.Helpers.initSpeechToText();
// Manage menu expanded/collapsed with templateCustomizer & local storage
//------------------------------------------------------------------
// If current layout is horizontal OR current window screen is small (overlay menu) than return from here
if (window.Helpers.isSmallScreen()) {
return;
}
// If current layout is vertical and current window screen is > small
// Auto update menu collapsed/expanded based on the themeConfig
window.Helpers.setCollapsed(true, false);
})();

304
views/files/md-block.js Normal file
View File

@@ -0,0 +1,304 @@
/**
* <md-block> custom element
* @author Lea Verou
*/
let marked = window.marked;
let DOMPurify = window.DOMPurify;
let Prism = window.Prism;
export const URLs = {
marked: "https://cdn.jsdelivr.net/npm/marked/src/marked.min.js",
DOMPurify: "https://cdn.jsdelivr.net/npm/dompurify@2.3.3/dist/purify.es.min.js"
}
// Fix indentation
function deIndent(text) {
let indent = text.match(/^[\r\n]*([\t ]+)/);
if (indent) {
indent = indent[1];
text = text.replace(RegExp("^" + indent, "gm"), "");
}
return text;
}
export class MarkdownElement extends HTMLElement {
constructor() {
super();
this.renderer = Object.assign({}, this.constructor.renderer);
for (let property in this.renderer) {
this.renderer[property] = this.renderer[property].bind(this);
}
}
get rendered() {
return this.getAttribute("rendered");
}
get mdContent () {
return this._mdContent;
}
set mdContent (html) {
this._mdContent = html;
this._contentFromHTML = false;
this.render();
}
connectedCallback() {
Object.defineProperty(this, "untrusted", {
value: this.hasAttribute("untrusted"),
enumerable: true,
configurable: false,
writable: false
});
if (this._mdContent === undefined) {
this._contentFromHTML = true;
this._mdContent = deIndent(this.innerHTML);
// https://github.com/markedjs/marked/issues/874#issuecomment-339995375
// marked expects markdown quotes (>) to be un-escaped, otherwise they won't render correctly
this._mdContent = this._mdContent.replace(/&gt;/g, '>');
}
this.render();
}
async render () {
if (!this.isConnected || this._mdContent === undefined) {
return;
}
if (!marked) {
marked = import(URLs.marked).then(m => m.marked);
}
marked = await marked;
marked.setOptions({
gfm: true,
smartypants: true,
langPrefix: "language-",
});
marked.use({renderer: this.renderer});
let html = this._parse();
if (this.untrusted) {
let mdContent = this._mdContent;
html = await MarkdownElement.sanitize(html);
if (this._mdContent !== mdContent) {
// While we were running this async call, the content changed
// We dont want to overwrite with old data. Abort mission!
return;
}
}
this.innerHTML = html;
if (!Prism && URLs.Prism && this.querySelector("code")) {
Prism = import(URLs.Prism);
if (URLs.PrismCSS) {
let link = document.createElement("link");
link.rel = "stylesheet";
link.href = URLs.PrismCSS;
document.head.appendChild(link);
}
}
if (Prism) {
await Prism; // in case it's still loading
Prism.highlightAllUnder(this);
}
if (this.src) {
this.setAttribute("rendered", this._contentFromHTML? "fallback" : "remote");
}
else {
this.setAttribute("rendered", this._contentFromHTML? "content" : "property");
}
// Fire event
let event = new CustomEvent("md-render", {bubbles: true, composed: true});
this.dispatchEvent(event);
}
static async sanitize(html) {
if (!DOMPurify) {
DOMPurify = import(URLs.DOMPurify).then(m => m.default);
}
DOMPurify = await DOMPurify; // in case it's still loading
return DOMPurify.sanitize(html);
}
};
export class MarkdownSpan extends MarkdownElement {
constructor() {
super();
}
_parse () {
return marked.parseInline(this._mdContent);
}
static renderer = {
codespan (code) {
if (this._contentFromHTML) {
// Inline HTML code needs to be escaped to not be parsed as HTML by the browser
// This results in marked double-escaping it, so we need to unescape it
code = code.replace(/&amp;(?=[lg]t;)/g, "&");
}
else {
// Remote code may include characters that need to be escaped to be visible in HTML
code = code.replace(/</g, "&lt;");
}
return `<code>${code}</code>`;
}
}
}
export class MarkdownBlock extends MarkdownElement {
constructor() {
super();
}
get src() {
return this._src;
}
set src(value) {
this.setAttribute("src", value);
}
get hmin() {
return this._hmin || 1;
}
set hmin(value) {
this.setAttribute("hmin", value);
}
get hlinks() {
return this._hlinks ?? null;
}
set hlinks(value) {
this.setAttribute("hlinks", value);
}
_parse () {
return marked.parse(this._mdContent);
}
static renderer = Object.assign({
heading (text, level, _raw, slugger) {
level = Math.min(6, level + (this.hmin - 1));
const id = slugger.slug(text);
const hlinks = this.hlinks;
let content;
if (hlinks === null) {
// No heading links
content = text;
}
else {
content = `<a href="#${id}" class="anchor">`;
if (hlinks === "") {
// Heading content is the link
content += text + "</a>";
}
else {
// Headings are prepended with a linked symbol
content += hlinks + "</a>" + text;
}
}
return `
<h${level} id="${id}">
${content}
</h${level}>`;
},
code (code, language, escaped) {
if (this._contentFromHTML) {
// Inline HTML code needs to be escaped to not be parsed as HTML by the browser
// This results in marked double-escaping it, so we need to unescape it
code = code.replace(/&amp;(?=[lg]t;)/g, "&");
}
else {
// Remote code may include characters that need to be escaped to be visible in HTML
code = code.replace(/</g, "&lt;");
}
return `<pre class="language-${language}"><code>${code}</code></pre>`;
}
}, MarkdownSpan.renderer);
static get observedAttributes() {
return ["src", "hmin", "hlinks"];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) {
return;
}
switch (name) {
case "src":
let url;
try {
url = new URL(newValue, location);
}
catch (e) {
return;
}
let prevSrc = this.src;
this._src = url;
if (this.src !== prevSrc) {
fetch(this.src)
.then(response => {
if (!response.ok) {
throw new Error(`Failed to fetch ${this.src}: ${response.status} ${response.statusText}`);
}
return response.text();
})
.then(text => {
this.mdContent = text;
})
.catch(e => {});
}
break;
case "hmin":
if (newValue > 0) {
this._hmin = +newValue;
this.render();
}
break;
case "hlinks":
this._hlinks = newValue;
this.render();
}
}
}
customElements.define("md-block", MarkdownBlock);
customElements.define("md-span", MarkdownSpan);

101
views/files/menu.js Normal file

File diff suppressed because one or more lines are too long

68
views/files/page-auth.css Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,211 @@
/*
* Container style
*/
.ps {
overflow: hidden !important;
overflow-anchor: none;
-ms-overflow-style: none;
touch-action: auto;
-ms-touch-action: auto;
}
/*
* Scrollbar rail styles
*/
.ps__rail-x {
display: none;
opacity: 0;
transition: background-color 0.2s linear, opacity 0.2s linear;
-webkit-transition: background-color 0.2s linear, opacity 0.2s linear;
height: 15px;
/* there must be 'bottom' or 'top' for ps__rail-x */
bottom: 0px;
/* please don't change 'position' */
position: absolute;
}
.ps__rail-y {
display: none;
opacity: 0;
transition: background-color 0.2s linear, opacity 0.2s linear;
-webkit-transition: background-color 0.2s linear, opacity 0.2s linear;
width: 15px;
/* there must be 'right' or 'left' for ps__rail-y */
right: 0;
/* please don't change 'position' */
position: absolute;
}
.ps--active-x > .ps__rail-x,
.ps--active-y > .ps__rail-y {
display: block;
background-color: transparent;
}
.ps:hover > .ps__rail-x,
.ps:hover > .ps__rail-y,
.ps--focus > .ps__rail-x,
.ps--focus > .ps__rail-y,
.ps--scrolling-x > .ps__rail-x,
.ps--scrolling-y > .ps__rail-y {
opacity: 0.6;
}
.ps .ps__rail-x:hover,
.ps .ps__rail-y:hover,
.ps .ps__rail-x:focus,
.ps .ps__rail-y:focus,
.ps .ps__rail-x.ps--clicking,
.ps .ps__rail-y.ps--clicking {
background-color: #eee;
opacity: 0.9;
}
/*
* Scrollbar thumb styles
*/
.ps__thumb-x {
background-color: #aaa;
border-radius: 6px;
transition: background-color 0.2s linear, height 0.2s ease-in-out;
-webkit-transition: background-color 0.2s linear, height 0.2s ease-in-out;
height: 6px;
/* there must be 'bottom' for ps__thumb-x */
bottom: 2px;
/* please don't change 'position' */
position: absolute;
}
.ps__thumb-y {
background-color: #aaa;
border-radius: 6px;
transition: background-color 0.2s linear, width 0.2s ease-in-out;
-webkit-transition: background-color 0.2s linear, width 0.2s ease-in-out;
width: 6px;
/* there must be 'right' for ps__thumb-y */
right: 2px;
/* please don't change 'position' */
position: absolute;
}
.ps__rail-x:hover > .ps__thumb-x,
.ps__rail-x:focus > .ps__thumb-x,
.ps__rail-x.ps--clicking .ps__thumb-x {
background-color: #999;
height: 11px;
}
.ps__rail-y:hover > .ps__thumb-y,
.ps__rail-y:focus > .ps__thumb-y,
.ps__rail-y.ps--clicking .ps__thumb-y {
background-color: #999;
width: 11px;
}
/* MS supports */
@supports (-ms-overflow-style: none) {
.ps {
overflow: auto !important;
}
}
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.ps {
overflow: auto !important;
}
}
.ps {
position: relative;
}
.ps__rail-x {
height: 0.25rem;
}
.ps__rail-y {
width: 0.25rem;
}
.ps__rail-x,
.ps__rail-y,
.ps__thumb-x,
.ps__thumb-y {
border-radius: 10rem;
}
.ps__rail-x:hover,
.ps__rail-x:focus,
.ps__rail-x.ps--clicking,
.ps__rail-x:hover > .ps__thumb-x,
.ps__rail-x:focus > .ps__thumb-x,
.ps__rail-x.ps--clicking > .ps__thumb-x {
height: 0.375rem;
}
.ps__rail-y:hover,
.ps__rail-y:focus,
.ps__rail-y.ps--clicking,
.ps__rail-y:hover > .ps__thumb-y,
.ps__rail-y:focus > .ps__thumb-y,
.ps__rail-y.ps--clicking > .ps__thumb-y {
width: 0.375rem;
}
.ps__thumb-x {
height: 0.25rem;
bottom: 0;
}
.ps__thumb-y {
width: 0.25rem;
right: 0;
}
.light-style .ps__thumb-x,
.light-style .ps__thumb-y {
background-color: rgba(67, 89, 113, 0.4);
}
.light-style .ps__rail-x:hover,
.light-style .ps__rail-y:hover,
.light-style .ps__rail-x:focus,
.light-style .ps__rail-y:focus,
.light-style .ps__rail-x.ps--clicking,
.light-style .ps__rail-y.ps--clicking {
background-color: rgba(67, 89, 113, 0.2);
}
.light-style .ps__rail-x:hover > .ps__thumb-x,
.light-style .ps__rail-y:hover > .ps__thumb-y,
.light-style .ps__rail-x:focus > .ps__thumb-x,
.light-style .ps__rail-y:focus > .ps__thumb-y,
.light-style .ps__rail-x.ps--clicking > .ps__thumb-x,
.light-style .ps__rail-y.ps--clicking > .ps__thumb-y {
background-color: rgba(67, 89, 113, 0.7);
}
.light-style .ps-inverted .ps__rail-x:hover,
.light-style .ps-inverted .ps__rail-y:hover,
.light-style .ps-inverted .ps__rail-x:focus,
.light-style .ps-inverted .ps__rail-y:focus,
.light-style .ps-inverted .ps__rail-x.ps--clicking,
.light-style .ps-inverted .ps__rail-y.ps--clicking {
background-color: rgba(255, 255, 255, 0.5);
}
.light-style .ps-inverted .ps__thumb-x,
.light-style .ps-inverted .ps__thumb-y {
background-color: rgba(255, 255, 255, 0.7);
}
.light-style .ps-inverted .ps__rail-x:hover > .ps__thumb-x,
.light-style .ps-inverted .ps__rail-y:hover > .ps__thumb-y,
.light-style .ps-inverted .ps__rail-x:focus > .ps__thumb-x,
.light-style .ps-inverted .ps__rail-y:focus > .ps__thumb-y,
.light-style .ps-inverted .ps__rail-x.ps--clicking > .ps__thumb-x,
.light-style .ps-inverted .ps__rail-y.ps--clicking > .ps__thumb-y {
background-color: #fff;
}
@supports (-moz-appearance: none) {
#both-scrollbars-example {
max-width: 1080px;
margin: 0 auto;
padding-left: 0;
padding-right: 0;
}
}

File diff suppressed because one or more lines are too long

112
views/files/popper.js Normal file

File diff suppressed because one or more lines are too long

128
views/files/portal.js Normal file
View File

@@ -0,0 +1,128 @@
$(async function(){
setI18NText(i18n_messages);
if('/docs.html' == window.location.pathname)return;
else if(sessionStorage.getItem('nickname') == null || sessionStorage.getItem('uid') == null ||
sessionStorage.getItem('nickname') == '' || sessionStorage.getItem('uid') == ''){
try{
var response = await fetch("/getusrinf", {
method: "POST",
cache: "no-cache"
});
if(response.status == 200){
let data = await response.json();
if(data.status == "OK"){
sessionStorage.setItem('nickname', getI18NJsonText(data.name));
sessionStorage.setItem('uid', data.uid);
sessionStorage.setItem('email', data.email);
}else{
window.location.href = "/login.html";
return;
}
}else{
window.location.href = "/login.html";
return;
}
}catch(err){
window.location.href = "/login.html";
return;
}
}
try{
var response = await fetch("/chksession", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
}
});
if(response.status == 200){
let data = await response.json();
if(data.status == "OK"){
//let user_id = data.user_id;
//$('#user_id').html(user_id);
}else{
window.location.href = "login.html";
}
}else{
window.location.href = "login.html";
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
if(document.getElementById("spinner"))document.getElementById("spinner").style.display = "none";
}
})
const i18n_messages = new Map();
var lang_map = new Map();
lang_map.set("en-US", "Conversations");
lang_map.set("zh-CN", "对话");
lang_map.set("ja", "会話");
i18n_messages.set("menu_conversation", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Space market");
lang_map.set("zh-CN", "空间市场");
lang_map.set("ja", "スペースマーケット");
i18n_messages.set("menu_market", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Space Settings");
lang_map.set("zh-CN", "空间设置");
lang_map.set("ja", "スペース設定");
i18n_messages.set("menu_settings", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Users");
lang_map.set("zh-CN", "用户");
lang_map.set("ja", "ユーザー");
i18n_messages.set("menu_mgr", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Space builder");
lang_map.set("zh-CN", "空间构建");
lang_map.set("ja", "スペースビルダー");
i18n_messages.set("menu_builder", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Documents");
lang_map.set("zh-CN", "文档");
lang_map.set("ja", "ドキュメント");
i18n_messages.set("menu_docs", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Contact us");
lang_map.set("zh-CN", "联系我们");
lang_map.set("ja", "お問い合わせ");
i18n_messages.set("title_contact", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Log out");
lang_map.set("zh-CN", "登 出");
lang_map.set("ja", "ログアウト");
i18n_messages.set("label_logout", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device Management");
lang_map.set("zh-CN", "设备管理");
lang_map.set("ja", "デバイス管理");
i18n_messages.set("label_device_mng", lang_map);
lang_map = new Map();
lang_map.set("en-US", "System error occurred. Please try again later");
lang_map.set("zh-CN", "系统出现错误,请稍后重试。");
lang_map.set("ja", "システムエラーが発生しました。後でもう一度お試しください");
i18n_messages.set("msg_sys_err", lang_map);
lang_map = new Map();
lang_map.set("en-US", "SisAiWorld is a comprehensive software platform built on AI models, enabling users to efficiently leverage various software functions and services through templates provided by AI experts. Additionally, SisAiWorld offers a specialized marketplace for templates and prompts, ensuring the protection of AI experts' intellectual property.");
lang_map.set("zh-CN", "SisAiWorld 是基于 AI 模型构建的综合软件平台,用户可以通过 AI 专家提供的模板有效地利用各种软件功能和服务。与此同时SisAiWorld 还提供了专门的模板和提示市场,能够确保 AI 专家的知识产权得到保护。");
lang_map.set("ja", "SisAiWorld は AI モデルに基づいて構築された包括的なソフトウェアプラットフォームで、AI 専門家によって提供されたテンプレートを使用して、さまざまなソフトウェア機能とサービスを効率的に活用できます。さらに、SisAiWorld は専門のテンプレートとプロンプトのマーケットプレイスを提供し、AI 専門家の知的財産権を保護できます。");
i18n_messages.set("site_desc", lang_map);

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 KiB

33
views/files/spinner.css Normal file
View File

@@ -0,0 +1,33 @@
.spinner {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9999;
display: none;
}
.spinner-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

File diff suppressed because one or more lines are too long

1212
views/files/ua-parser.js Normal file

File diff suppressed because it is too large Load Diff

399
views/login.html Normal file
View File

@@ -0,0 +1,399 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style customizer-hide"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>Login page - SisAi world </title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<!-- link rel="stylesheet" href="../assets/vendor/fonts/boxicons.css" / -->
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<!-- Page CSS -->
<!-- Page -->
<link rel="stylesheet" href="files/page-auth.css" />
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<link rel="stylesheet" href="files/spinner.css" />
<script src="files/amipro_utils.js?v=20230414"></script>
<script>
const i18n_messages = new Map();
lang_map = new Map();
lang_map.set("en-US", "System error occurred. Please try again later");
lang_map.set("zh-CN", "系统出现错误,请稍后重试。");
lang_map.set("ja", "システムエラーが発生しました。後でもう一度お試しください");
i18n_messages.set("msg_sys_err", lang_map);
var lang_map = new Map();
lang_map.set("en-US", "Welcome to SisAi world!");
lang_map.set("zh-CN", "欢迎来到SisAi世界");
lang_map.set("ja", "SisAiワールドへようこそ");
i18n_messages.set("msg_welcome", lang_map);
lang_map = new Map();
lang_map.set("en-US", "EMAIL ADDRESS");
lang_map.set("zh-CN", "邮件地址");
lang_map.set("ja", "メールアドレス");
i18n_messages.set("msg_uid", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Input correct Email address, please!");
lang_map.set("zh-CN", "请输入准确邮件地址!");
lang_map.set("ja", "メールアドレスを正しく入力してください!");
i18n_messages.set("msg_uid_input", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Input password, please!");
lang_map.set("zh-CN", "请输入密码!");
lang_map.set("ja", "パスワードを入力してください!");
i18n_messages.set("msg_pw_input", lang_map);
lang_map = new Map();
lang_map.set("en-US", "PASSWORD");
lang_map.set("zh-CN", "用户密码");
lang_map.set("ja", "パスワード");
i18n_messages.set("msg_pw", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Login");
lang_map.set("zh-CN", "登 录");
lang_map.set("ja", "ログイン");
i18n_messages.set("title_fido2_login", lang_map);
lang_map = new Map();
lang_map.set("en-US", "The account or password is incorrect, please try again.");
lang_map.set("zh-CN", "账号或密码错误,请重试。");
lang_map.set("ja", "アカウントまたはパスワードが間違っています。再試行してください");
i18n_messages.set("login_err_retry", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "免费创建账号");
lang_map.set("ja", "無料でアカウントを作成");
lang_map.set("en-US", "Create free account");
i18n_messages.set("title_new_account", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "忘记密码");
lang_map.set("ja", "パスワードを忘れた場合");
lang_map.set("en-US", "Forgot password");
i18n_messages.set("title_reentry_pw", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Sent register email from support@amipro.me.\n"+
"Please check your email and click the link to register your account.\n"+
"Please check spam folder if you can't find the email.");
lang_map.set("zh-CN", "已经从 support@amipro.me 发送创建新账号邮件到您的邮箱,\n请查收邮件并点击邮件中的链接来创建新账号。\n"+
"如果您没有收到邮件,请检查垃圾邮件文件夹。");
lang_map.set("ja", "support@amipro.meからアカウント作成メールを送信しました、\n"+
"メールを確認し、リンクをクリックしてアカウントを作成してください。\n"+
"メールが見つからない場合は、迷惑メール フォルダを確認してください。");
i18n_messages.set("title_sent_reg_email", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Sent re-enter password email from support@amipro.me.\n"+
"Please check your email and click the link to re-enter your password.\n"+
"Please check spam folder if you can't find the email.");
lang_map.set("zh-CN", "已经从 support@amipro.me 发送重置密码邮件到您的邮箱,\n请查收邮件并点击邮件中的链接来重新设置您的密码。\n"+
"如果您没有收到邮件,请检查垃圾邮件文件夹。");
lang_map.set("ja", "support@amipro.meからパスワード設定メールを送信しました、\n"+
"メールを確認し、リンクをクリックしてパスワードを再設定してください。\n"+
"メールが見つからない場合は、迷惑メール フォルダを確認してください。");
i18n_messages.set("title_sent_pw_email", lang_map);
lang_map = new Map();
lang_map.set("en-US", "System error, please try again later.");
lang_map.set("zh-CN", "系统错误,请稍后重试。");
lang_map.set("ja", "システムエラーが発生しました。しばらくしてもう一度お試しください。");
i18n_messages.set("title_syserr_retry", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Contact us");
lang_map.set("zh-CN", "联系我们");
lang_map.set("ja", "お問い合わせ");
i18n_messages.set("title_contact", lang_map);
window.onload = async function() {
setI18NText(i18n_messages)
await logout();
sessionStorage.clear();
}
async function logout() {
try{
var response = await fetch("/logout", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
}
});
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
function checkInput(){
const uid = $('#uid').val().trim()
if(!checkEmailFormat(uid)){
alert($('#msg_uid_input').html())
return false;
}
const pw = $('#password').val().trim()
if(!pw || 0>=pw.length){
alert($('#msg_pw_input').html())
return false;
}
return true;
}
function checkEmailFormat(email){
if(!email || 0>=email.length)return false;
if(!email.match(/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/))return false;
return true;
}
var is_reset_pw = false;
async function createAccount(reset_pw){
is_reset_pw = reset_pw;
var email = $('#uid').val().trim()
if(!email || !checkEmailFormat(email)){
email = prompt(getI18NText(i18n_messages, 'msg_uid'));
if (!email) return;
if(!checkEmailFormat(email)){
alert(getI18NText(i18n_messages, 'msg_uid_input'));
return;
}
}
showSpinner();
email = email.trim()
try{
const response = await fetch("/sendregmail", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({email: email, language: navigator.language})
})
hideSpinner();
if(response.status == 200){
const resp = await response.json();
if (resp.status!='OK') {
alert(getI18NText(i18n_messages, 'title_syserr_retry'));
}else{
if(is_reset_pw){
alert(getI18NText(i18n_messages, 'title_sent_pw_email'));
}else{
alert(getI18NText(i18n_messages, 'title_sent_reg_email'));
}
}
}else{
alert(getI18NText(i18n_messages, 'title_syserr_retry'));
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
function showSpinner() {
$('#title_fido2_login').prop('disabled', true);
$('#title_new_account').prop('disabled', true);
$('#title_reentry_pw').prop('disabled', true);
document.getElementById("spinner").style.display = "block";
}
function hideSpinner() {
$('#title_fido2_login').prop('disabled', false);
$('#title_new_account').prop('disabled', false);
$('#title_reentry_pw').prop('disabled', false);
document.getElementById("spinner").style.display = "none";
}
async function authenticate(){
showSpinner();
var email = $('#uid').val().trim()
var pw = $('#password').val().trim()
if(!email || 0>=email.length){
alert($('#msg_uid_input').html())
hideSpinner();
return;
}else if(!pw || 0>=pw.length){
alert($('#msg_pw_input').html())
hideSpinner();
return;
}
email = email.toLowerCase();
try{
const response = await fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({email: email, pwd: pw}),
cache: "no-cache"
})
if(response.status == 200){
const resp = await response.json();
if (resp.status!='OK') {
alert(getI18NText(i18n_messages, 'login_err_retry'));
hideSpinner();
}else{
const nm = getI18NJsonText(resp.nickname);
sessionStorage.setItem('nickname', nm);
sessionStorage.setItem('email', email);
sessionStorage.setItem('uid', resp.uid);
window.location.href = "top.html";
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
</script>
</head>
<body>
<!-- Content -->
<div class="container-xxl">
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner">
<!-- Register -->
<div class="card">
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center">
<a href="https://www.amiPro.me/" class="app-brand-link gap-2">
<img style="width:160px;" src="files/favicon.ico"/>
</a>
</div>
<!-- /Logo -->
<h4 class="mb-2" id="msg_welcome">Welcome to SisAi world!</h4>
<p class="mb-4" id="msg_intro"></p>
<div id="msg_uid_input" style="display:none;">Input User ID, please!</div>
<div id="msg_pw_input" style="display:none;">Input User ID, please!</div>
<form class="mb-3" action="javascript:authenticate();" method="POST"> <!--onsubmit="return checkInput();" -->
<div class="mb-3">
<label for="uid" class="form-label" id="msg_uid" name="uid">User ID</label>
<input
type="text"
class="form-control"
id="uid"
name="uid"
placeholder="Enter your email address"
autofocus
/>
</div>
<div class="mb-3 form-password-toggle">
<div class="d-flex justify-content-between">
<label class="form-label" for="password" id="msg_pw">Password</label>
</div>
<div class="input-group input-group-merge">
<input
type="password"
id="password"
class="form-control"
name="password"
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
aria-describedby="password"
/>
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-3">
<button class="btn btn-primary d-grid w-100" id="title_fido2_login">Sign in</button>
</div>
</form>
<p class="text-center" id="fido_btn">
<a href="javascript:createAccount(false);">
<span id="title_new_account">Create account</span>
</a>
</p>
<p class="text-center" id="fido_btn">
<a href="javascript:createAccount(true);">
<span id="title_reentry_pw">Re-entry password</span>
</a>
</p>
<div style="margin-left: 60%;">
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</div>
<!-- /Register -->
</div>
</div>
</div>
<div id="spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<!-- / Content -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

506
views/market.html Normal file
View File

@@ -0,0 +1,506 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>SisAi World</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<script src="files/jquery.js"></script>
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/ua-parser.js"></script>
<link rel="stylesheet" href="files/spinner.css" />
<script src="files/amipro_utils.js?v=20230401402"></script>
<script src="files/portal.js"></script>
<style>
.scrollable_intro {
height: 160px;
overflow: auto;
}
</style>
<script>
lang_map = new Map();
lang_map.set("en-US", "Total registrations");
lang_map.set("zh-CN", "总注册数");
lang_map.set("ja", "総登録数");
i18n_messages.set("label_total_reg", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Total authentications");
lang_map.set("zh-CN", "总认证数");
lang_map.set("ja", " 総認証数");
i18n_messages.set("label_total_auth", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Failed authentications");
lang_map.set("zh-CN", "认证失败数");
lang_map.set("ja", "認証失敗数");
i18n_messages.set("label_total_auth_fail", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Failed registrations");
lang_map.set("zh-CN", "注册失败数");
lang_map.set("ja", "登録失敗数");
i18n_messages.set("label_total_reg_fail", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Action list");
lang_map.set("zh-CN", "操作列表");
lang_map.set("ja", "アクションリスト");
i18n_messages.set("label_action_list", lang_map);
lang_map = new Map();
lang_map.set("en-US", "All domains");
lang_map.set("zh-CN", "所有域");
lang_map.set("ja", "すべてのドメイン");
i18n_messages.set("label_all_domains", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Search...");
lang_map.set("zh-CN", "搜索...");
lang_map.set("ja", "検索...");
i18n_messages.set("label_search", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Failed actions only");
lang_map.set("zh-CN", "仅显示失败操作");
lang_map.set("ja", "失敗したアクションのみ");
i18n_messages.set("label_fail_only", lang_map);
lang_map = new Map();
lang_map.set("en-US", "System error. Please try later.");
lang_map.set("zh-CN", "系统错误,请稍后再试。");
lang_map.set("ja", "システムエラー。後でもう一度お試しください。");
i18n_messages.set("msg_sys_fail", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Please input the space name");
lang_map.set("zh-CN", "请输入空间名称");
lang_map.set("ja", "スペース名を入力してください");
i18n_messages.set("msg_no_spacenm", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Loading more");
lang_map.set("zh-CN", "加载更多");
lang_map.set("ja", "もっと読み込む");
i18n_messages.set("title_list_more", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Create space");
lang_map.set("zh-CN", "创建空间");
lang_map.set("ja", "スペースを作成する");
i18n_messages.set("label_create", lang_map);
lang_map = new Map();
lang_map.set("en-US", "The name has been used. Please input another one.");
lang_map.set("zh-CN", "名称已被使用,请输入其他名称");
lang_map.set("ja", "名前が使用されています。別の名前を入力してください。");
i18n_messages.set("msg_same_name", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "公开模板");
lang_map.set("ja", "公開テンプレート");
lang_map.set("en-US", "Public templates");
i18n_messages.set("opt_type_public", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "我的模板");
lang_map.set("ja", "私のテンプレート");
lang_map.set("en-US", "My templates");
i18n_messages.set("opt_type_myown", lang_map);
var cur_tmpl_page = 0;
const page_length = 12;
$(async function () {
$("#user_nickname").text(sessionStorage.getItem("nickname")?sessionStorage.getItem("nickname"):"");
listTemplates();
});
async function listTemplates(){
showSpinner();
cur_tmpl_page++;
try{
var type = $("#sel_type").val();
var response = await fetch("/listspacetmpls", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({type: type, page:cur_tmpl_page, per_page:page_length}),
});
if(response.status == 200){
const resp = await response.json();
var lst_html = '';
for(var i = 0; i < resp.tmpls.length; i++){
var tmpl = resp.tmpls[i];
lst_html += '<div class="col-lg-4 col-md-6 col-12 mb-4"><div class="card"><div class="card-body"><h5 class="card-title">'+
getI18NJsonText(tmpl.spacetemp_name) +'</h5>'+ '<h6>'+ getI18NJsonText(tmpl.creator_name)+'</h6>'+
'<p class="card-text scrollable_intro">'+ getI18NJsonText(tmpl.spacetemp_intro).replace(/(?:\r\n|\r|\n)/g, '<br>') +'</p><a href="#" onclick="javascript:createSpace(\''+
tmpl.spacetemp_id +'\'); return false;" class="btn btn-primary" target="_blank">'+
getI18NText(i18n_messages, 'label_create')+'</a></div></div></div>';
}
$("#tmpls_rows").append(lst_html);
if(resp.tmpls.length < page_length){
$("#div_list_more").hide();
}else{
$("#div_list_more").show();
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
async function changedType(){
cur_tmpl_page = 0;
$("#tmpls_rows").empty();
listTemplates();
}
async function createSpace(spacetemp_id){
try{
//Pop a dialog to input the space name
var space_name = prompt(getI18NText(i18n_messages, 'label_create'));
if(space_name == null) return;
if(space_name == ""){
alert(getI18NText(i18n_messages, 'msg_no_spacenm'));
}else{
var response = await fetch("/chksamenm", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
type: 'space',
space_id: '--create--',
space_name: space_name
}),
});
if(response.status == 200){
const resp = await response.json();
if(resp.status == "OK"){
if(resp.same == true){
alert(getI18NText(i18n_messages, 'msg_same_name'));
return;
}
}else window.location.href = "/login.html";
}else {
window.location.href = "/login.html";
}
showSpinner();
var space_name_json = {};
space_name_json[window.navigator.language] = space_name;
response = await fetch("/createspace", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
spacetemp_id: spacetemp_id,
space_name: space_name_json
}),
});
if(response.status == 200){
const resp = await response.json();
if(resp.status == "OK"){
//Redirect to the setting page
window.location.href = "/settings.html?sid="+resp.space_id;
return
}else{
window.location.href = "/login.html";
return
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
hideSpinner();
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
function showSpinner() {
document.getElementById("spinner").style.display = "block";
}
function hideSpinner() {
document.getElementById("spinner").style.display = "none";
}
</script>
</head>
<body>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="top.html" class="app-brand-link">
<span class="app-brand-logo demo">
<img src="files/favicon.ico" alt="SisAi" width="45%" style="display: block; margin: 10%;" />
</span>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<li class="menu-item">
<a href="top.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics" id="menu_conversation">Conversation</div>
</a>
</li>
<li class="menu-item">
<a href="settings.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div data-i18n="Analytics" id="menu_settings">Settings</div>
</a>
</li>
<li class="menu-item active">
<a href="market.html" class="menu-link" onclick="javascript:return false;">
<i class="menu-icon tf-icons bx bx-store-alt"></i>
<div data-i18n="Analytics" id="menu_market">market</div>
</a>
</li>
<li class="menu-item">
<a href="builder.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-wrench"></i>
<div data-i18n="Analytics" id="menu_builder">Space Builder</div>
</a>
</li>
<li class="menu-item">
<a href="mgr.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-user"></i>
<div data-i18n="Analytics" id="menu_mgr">Managers</div>
</a>
</li>
<li class="menu-item">
<a href="docs.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-file"></i>
<div data-i18n="Analytics" id="menu_docs">Documents</div>
</a>
</li>
</ul>
</aside>
<!-- / Menu -->
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
SisAi World
</div>
</div>
<select class="form-select" style="width: 130pt;margin-left: auto;" id="sel_type" aria-label="selected type" onchange="changedType();">
<option value="public" id="opt_type_public">Public</option>
<option value="myown" id="opt_type_myown">My templates</option>
</select>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="javascript:void(0);" data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block" id="user_nickname">A manager</span>
<!--small class="text-muted">Admin</small -->
</div>
</div>
</a>
</li>
<!--li>
<div class="dropdown-divider"></div>
</!--li>
<li>
<a class="dropdown-item" href="devices.html">
<i class="bx bx-mobile-alt me-2"></i>
<span class="align-middle" id="label_device_mng">Device management</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<span class="d-flex align-items-center align-middle">
<i class="flex-shrink-0 bx bx-credit-card me-2"></i>
<span class="flex-grow-1 align-middle">Billing</span>
<span class="flex-shrink-0 badge badge-center rounded-pill bg-danger w-px-20 h-px-20">!</span>
</span>
</a>
</li -->
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="login.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle" id="label_logout">Log Out</span>
</a>
</li>
</ul>
</li>
<!--/ User -->
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row" id="tmpls_rows">
<!-- templates list -->
</div>
<div class="mb-3" style="width: 30%; margin-left: 35%;" id="div_list_more">
<button class="btn btn-info d-grid w-100" id="title_list_more" onclick="listTemplates();"></button>
</div>
</div>
<div id="spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered By OpenAI-4o)</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- Core JS -->
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<!-- endbuild -->
<!-- Page JS -->
<script src="files/dashboards-analytics.js"></script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

868
views/mgr.html Normal file
View File

@@ -0,0 +1,868 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>SisAi world</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<script src="files/jquery.js"></script>
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/ua-parser.js"></script>
<link rel="stylesheet" href="files/spinner.css" />
<script src="files/amipro_utils.js?v=20230401402"></script>
<!-- script src="files/dfido2-lib.js?v=20230813"></!-->
<script src="files/portal.js?v=20230828"></script>
<script>
lang_map = new Map();
lang_map.set("en-US", "Total users");
lang_map.set("zh-CN", "总用户数");
lang_map.set("ja", "総ユーザー数");
i18n_messages.set("label_total_users", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Failed authentications in past weeks");
lang_map.set("zh-CN", "最近4周失败认证数");
lang_map.set("ja", "直近4週間失敗認証数");
i18n_messages.set("label_fail_auth", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Total authentications in past weeks");
lang_map.set("zh-CN", "最近4周总认证数");
lang_map.set("ja", "直近4週間総認証数");
i18n_messages.set("label_total_auth", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Active users in past weeks");
lang_map.set("zh-CN", "最近4周活跃用户数");
lang_map.set("ja", "直近4週間アクティブユーザー数");
i18n_messages.set("label_active_users", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Users list");
lang_map.set("zh-CN", "用户列表");
lang_map.set("ja", "ユーザーリスト");
i18n_messages.set("label_users_list", lang_map);
lang_map = new Map();
lang_map.set("en-US", "User email address");
lang_map.set("zh-CN", "用户邮件地址");
lang_map.set("ja", "ユーザーメールアドレス");
i18n_messages.set("label_mgr_email", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Role");
lang_map.set("zh-CN", "角色");
lang_map.set("ja", "ロール");
i18n_messages.set("label_mgr_role", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Manager");
lang_map.set("zh-CN", "管理员");
lang_map.set("ja", "管理者");
i18n_messages.set("sel_mgr_role_editor", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "邀请的空间");
lang_map.set("ja", "招待されたスペース");
lang_map.set("en-US", "Invited space");
i18n_messages.set("label_sel_invt_space", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "普通用户");
lang_map.set("ja", "一般ユーザー");
lang_map.set("en-US", "Normal user");
i18n_messages.set("sel_mgr_role_viewer", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Invite User");
lang_map.set("zh-CN", "邀请用户");
lang_map.set("ja", "ユーザー招待");
i18n_messages.set("btn_add", lang_map);
lang_map = new Map();
lang_map.set("en-US", "All domains");
lang_map.set("zh-CN", "所有域");
lang_map.set("ja", "すべてのドメイン");
i18n_messages.set("label_all_domains", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Search...");
lang_map.set("zh-CN", "搜索...");
lang_map.set("ja", "検索...");
i18n_messages.set("label_search", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Input correct Email address, please!");
lang_map.set("zh-CN", "请输入准确邮件地址!");
lang_map.set("ja", "メールアドレスを正しく入力してください!");
i18n_messages.set("msg_mgr_input", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Cannot invite yourself!");
lang_map.set("zh-CN", "不能邀请自己!");
lang_map.set("ja", "自分を招待することはできません!");
i18n_messages.set("msg_mgr_self", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Sent inviting email from support@amipro.me.\n"+
"Please let user to check email and click the link to join.\n"+
"Please check spam folder if user can't find the email.");
lang_map.set("zh-CN", "已经从 support@amipro.me 发送邀请邮件到用户的邮箱,\n请通知用户查收邮件并点击邮件中的链接来接受邀请。\n"+
"如果没有收到邮件,请用户检查垃圾邮件文件夹。");
lang_map.set("ja", "support@amipro.meから招待メールを送信しました、\n"+
"電子メールを確認し、電子メール内のリンクをクリックして招待を受け入れるようにユーザーに通知してください。\n"+
"メールが見つからない場合は、迷惑メール フォルダを確認してください。");
i18n_messages.set("title_sent_invite_email", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "从空间移除");
lang_map.set("ja", "スペースから削除");
lang_map.set("en-US", "Remove from space");
i18n_messages.set("btn_unspace", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Recovery link");
lang_map.set("zh-CN", "恢复链接");
lang_map.set("ja", "リカバリー・リンク");
i18n_messages.set("btn_link", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Users can register their devices by clicking this link without authentication. Please send this link to users with a secure method. The link will be expired in 15 minutes.\n(Link has been copied to clipboard)");
lang_map.set("zh-CN", "用户可以无需进行身份验证即可通过点击此链接而注册其设备。请通过安全的方式将此链接发送给用户。链接将在15分钟后过期。\n链接已经拷贝至剪贴板");
lang_map.set("ja", "ユーザーは、このリンクをクリックすることで、認証なしにデバイスを登録できます。このリンクを安全な方法でユーザーに送信してください。リンクは15分で期限切れになります。\nリンクはクリップボードにコピーされました");
i18n_messages.set("msg_link", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Devices");
lang_map.set("zh-CN", "设备");
lang_map.set("ja", "デバイス");
i18n_messages.set("btn_devices", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Are you sure to remove this account from group?");
lang_map.set("zh-CN", "确定要移出此账户吗?");
lang_map.set("ja", "このアカウントをグループから削除してもよろしいですか?");
i18n_messages.set("msg_remove_account", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Are you sure to delete this device?");
lang_map.set("zh-CN", "确定要删除此设备吗?");
lang_map.set("ja", "このデバイスを削除してもよろしいですか?");
i18n_messages.set("msg_del_device", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Delete");
lang_map.set("zh-CN", "删除");
lang_map.set("ja", "削除");
i18n_messages.set("btn_delete", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Failed to delete device");
lang_map.set("zh-CN", "删除设备失败");
lang_map.set("ja", "デバイスの削除に失敗しました");
i18n_messages.set("msg_del_dvc_fail", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Failed to remove account from group");
lang_map.set("zh-CN", "账户移出组失败");
lang_map.set("ja", "アカウントをグループから削除できませんでした");
i18n_messages.set("msg_remove_acc_fail", lang_map);
lang_map = new Map();
lang_map.set("en-US", "System error. Please try later.");
lang_map.set("zh-CN", "系统错误,请稍后再试。");
lang_map.set("ja", "システムエラー。後でもう一度お試しください。");
i18n_messages.set("msg_sys_fail", lang_map);
lang_map = new Map();
lang_map.set("en-US", "No devices");
lang_map.set("zh-CN", "尚无设备");
lang_map.set("ja", "デバイスなし");
i18n_messages.set("msg_no_device", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "尚无可以管理的空间,请到空间市场中创建");
lang_map.set("ja", "管理できるスペースがありません。スペースマーケットで作成してください。");
lang_map.set("en-US", "No space to manage, please create in space market.");
i18n_messages.set("label_no_space", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Space market");
lang_map.set("zh-CN", "空间市场");
lang_map.set("ja", "スペースマーケット");
i18n_messages.set("label_market", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "确定要修改自身的权限吗?");
lang_map.set("ja", "自分の権限を変更してもよろしいですか?");
lang_map.set("en-US", "Are you sure to change your role?");
i18n_messages.set("msg_chg_self_role", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "确定将此账号从该空间移除吗?");
lang_map.set("ja", "このアカウントをこのスペースから削除してもよろしいですか?");
lang_map.set("en-US", "Are you sure to remove this account from this space?");
i18n_messages.set("msg_unspace", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "确定将自己的账号从该空间移除吗?");
lang_map.set("ja", "自分のアカウントをこのスペースから削除してもよろしいですか?");
lang_map.set("en-US", "Are you sure to remove your account from this space?");
i18n_messages.set("msg_unspace_self", lang_map);
$(async function () {
$("#user_nickname").text(sessionStorage.getItem("nickname")?sessionStorage.getItem("nickname"):"");
showSpinner();
await listSpaces();
if($('#sel_invt_space').children().length == 0){
$('#no_space_table').show();
$('#invt_table').hide();
}else{
$('#no_space_table').hide();
$('#invt_table').show();
}
//$('#input_search').prop('placeholder', getI18NText(i18n_messages, 'label_search'));
/*$('#input_search').on('keypress', function(e) {
if (e.keyCode === 13) {
changed_search = true;
listMgrs();
}
});*/
listSpaceUsers();
hideSpinner();
});
async function listSpaces(){
showSpinner();
var lstJson = {};
lstJson["role_type"] = 1;
const response = await fetch("/listspacenms", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
var sel_html = '';
if(resp.spaces.length == 0){
$('#no_space_table').show();
$('#invt_table').hide();
}else{
for(let space of resp.spaces){
sel_html += '<option value="'+space.space_id+'">'+ getI18NJsonText(space.space_name)+'</option>';
}
$('#sel_invt_space').html(sel_html);
}
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}
}else if(response.status == 440){
window.location.href = "/login.html";
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}
hideSpinner();
}
async function inviteMgr(){
if(checkInput()){
showSpinner();
var lstJson = {};
lstJson["email"] = $('#input_mgr_email').val().trim();
lstJson["role"] = $('#sel_mgr_role').val();
lstJson["space_id"] = $('#sel_invt_space').val();
try{
const response = await fetch("/sendinvtmail", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
hideSpinner();
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
alert(getI18NText(i18n_messages, 'title_sent_invite_email'));
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
}
function checkInput(){
const mgremail = $('#input_mgr_email').val().trim()
if(!checkEmailFormat(mgremail)){
alert($('#msg_mgr_input').html())
return false;
}
else if(mgremail == sessionStorage.getItem('email')){
alert($('#msg_mgr_self').html())
return false;
}else return true;
}
//Copy text to clipboard
function copyToClipboard(text) {
setTimeout(function(){
var dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}, 300);
}
var changed_search = false;
async function listSpaceUsers(){
showSpinner();
$('#div_list_more').hide();
/*if($("#input_search").val()){
lstJson["search"] = encodeURIComponent($("#input_search").val());
}*/
/*if(changed_search){
$("#users_list").html('');
changed_search = false;
}*/
$("#users_list").html('');
var resp = await fetch("/listspcusrs", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({})
});
const result = await resp.json();
if ('OK' === result.status) {
let lst_html = $("#users_list").html();
for(let space of result.list){
var users_html = '';
//Find my self role
var my_role = 0;
for(let user of space.users){
if(user.account_id == sessionStorage.getItem("uid")){
my_role = user.role;
break;
}
}
for(let user of space.users){
var map = new Map();
map.set("user_name", getI18NJsonText(user.account_name));
map.set("user_email", user.account_email);
map.set("space_id", space.space_id);
map.set("user_id", user.account_id);
map.set("label_normal_user", getI18NText(i18n_messages, 'sel_mgr_role_viewer'));
map.set("label_manager", getI18NText(i18n_messages, 'sel_mgr_role_editor'));
map.set("label_remove", getI18NText(i18n_messages, 'btn_unspace'));
map.set("selected_0", user.role == 0?"selected":"");
map.set("selected_1", user.role == 1?"selected":"");
if(my_role == 1){
users_html += replaceTemplate(document.getElementById('list_user_template_mgr').innerHTML, map);
}else{
users_html += replaceTemplate(document.getElementById('list_user_template_user').innerHTML, map);
}
}
var map = new Map();
map.set("space_id", space.space_id);
map.set("space_name", getI18NJsonText(space.space_name));
map.set("user_list", "<table style='width:98%;'>"+users_html+"</table>");
lst_html += replaceTemplate(document.getElementById('list_space_template').innerHTML, map);
}
document.getElementById('users_list').innerHTML = lst_html;
}
/*else{
const msg = getI18NErrorMessage(result.errorMessage);
alert(msg?msg:result.errorMessage);
}*/
//if(result.users && result.users.length >= lstJson["limit"])$('#div_list_more').show();
hideSpinner();
}
async function changedRole(space_id, user_id){
if(user_id == sessionStorage.getItem('uid') && !confirm(getI18NText(i18n_messages, 'msg_chg_self_role'))){
window.location.reload();
return;
}
var lstJson = {};
lstJson["space_id"] = space_id;
lstJson["user_id"] = user_id;
lstJson["role"] = $('#sel_role_'+space_id+'_'+user_id).val();
const response = await fetch("/chgrlusr", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
//alert('Role changed');
if(user_id == sessionStorage.getItem('uid')){
window.location.reload();
}
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
window.location.reload();
}
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
window.location.reload();
}
}
async function unspaceUser(space_id, user_id){
var msgtxt;
if(user_id == sessionStorage.getItem('uid'))msgtxt = getI18NText(i18n_messages, 'msg_unspace_self');
else msgtxt = getI18NText(i18n_messages, 'msg_unspace');
if(!confirm(msgtxt)){
return;
}
showSpinner();
var lstJson = {};
lstJson["space_id"] = space_id;
lstJson["user_id"] = user_id;
const response = await fetch("/unspcusr", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
//alert('User removed');
if(user_id == sessionStorage.getItem('uid')){
window.location.reload();
}else $('#user_tr_'+space_id+"_"+user_id).remove();
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}
}else{
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}
hideSpinner();
}
function replaceTemplate(template, replaceMap){
var newTemplate = template;
for (var [key, value] of replaceMap) {
newTemplate = newTemplate.replace(new RegExp('\\['+key+'\\]', 'g'), value);
}
return newTemplate;
}
function checkEmailFormat(email){
if(!email || 0>=email.length)return false;
if(!email.match(/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/))return false;
return true;
}
//Copy text to clipboard
function copyToClipboard(text) {
setTimeout(function(){
var dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}, 300);
}
function showSpinner() {
$('#sel_domain').prop('disabled', true);
document.getElementById("spinner").style.display = "block";
}
function hideSpinner() {
$('#sel_domain').prop('disabled', false);
document.getElementById("spinner").style.display = "none";
}
</script>
</head>
<body>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="top.html" class="app-brand-link">
<span class="app-brand-logo demo">
<img src="files/favicon.ico" alt="SisAi" width="45%" style="display: block; margin: 10%;" />
</span>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<li class="menu-item">
<a href="top.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics" id="menu_conversation">Conversation</div>
</a>
</li>
<li class="menu-item">
<a href="settings.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div data-i18n="Analytics" id="menu_settings">Settings</div>
</a>
</li>
<li class="menu-item">
<a href="market.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-store-alt"></i>
<div data-i18n="Analytics" id="menu_market">market</div>
</a>
</li>
<li class="menu-item">
<a href="builder.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-wrench"></i>
<div data-i18n="Analytics" id="menu_builder">Space Builder</div>
</a>
</li>
<li class="menu-item active">
<a href="mgr.html" class="menu-link" onclick="javascript:return false;">
<i class="menu-icon tf-icons bx bx-user"></i>
<div data-i18n="Analytics" id="menu_mgr">Managers</div>
</a>
</li>
<li class="menu-item">
<a href="docs.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-file"></i>
<div data-i18n="Analytics" id="menu_docs">Documents</div>
</a>
</li>
</ul>
</aside>
<!-- / Menu -->
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
SisAi World
</div>
</div>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="javascript:void(0);" data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block" id="user_nickname">A manager</span>
<!-- small class="text-muted">Admin</!-->
</div>
</div>
</a>
</li>
<!-- li>
<div class="dropdown-divider"></div>
</>
<li>
<a class="dropdown-item" href="devices.html">
<i class="bx bx-mobile-alt me-2"></i>
<span class="align-middle" id="label_device_mng">Device management</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<span class="d-flex align-items-center align-middle">
<i class="flex-shrink-0 bx bx-credit-card me-2"></i>
<span class="flex-grow-1 align-middle">Billing</span>
<span class="flex-shrink-0 badge badge-center rounded-pill bg-danger w-px-20 h-px-20">!</span>
</span>
</a>
</li -->
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="login.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle" id="label_logout">Log Out</span>
</a>
</li>
</ul>
</li>
<!--/ User -->
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
<div id="msg_mgr_input" style="display:none;">Input email address, please!</div>
<div id="msg_mgr_self" style="display:none;">Cannot invite yourself!</div>
<div class="row">
<div class="col-md-12 col-lg-12 order-2 mb-4">
<div class="card h-100">
<div class="card-header d-flex align-items-center justify-content-between">
<h5 class="card-title m-0 me-2" id="label_users_list">Manager list</h5>
<div id="no_space_table" style="display: none;">
<h6 id="label_no_space" >No space, please create in space market.</h6>
<a href="market.html" style="font-size: larger;" id="label_market">market</a>
</div>
<div class="row" style="width: 90%;" id="invt_table">
<div class="col-md-6 col-lg-3 order-2 mb-4">
<div class="form-floating">
<input type="text" class="form-control" id="input_mgr_email" placeholder="email address">
<label for="input_mgr_email" id="label_mgr_email">Email address</label>
</div>
</div>
<div class="col-md-6 col-lg-3 order-2 mb-4">
<div class="form-floating">
<select class="form-select" id="sel_invt_space" aria-label="Users' space">
</select>
<label for="sel_invt_space" id="label_sel_invt_space">Invite to space</label>
</div>
</div>
<div class="col-md-6 col-lg-3 order-2 mb-4">
<div class="form-floating">
<select class="form-select" id="sel_mgr_role" aria-label="Users' role">
<option value="0" id="sel_mgr_role_viewer">User</option>
<option value="1" id="sel_mgr_role_editor">Manager</option>
</select>
<label for="sel_mgr_role" id="label_mgr_role">Manager's role</label>
</div>
</div>
<div class="col-md-6 col-lg-3 order-2 mb-4" style="text-align: right;">
<button style="white-space:nowrap;height: 3.5rem;width: 90%; max-width: 200pt;" class="btn btn-info" onclick="inviteMgr();" id="btn_add">Invite manager</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="list_user_template_mgr" style="display: none;">
<div class="row" id="user_tr_[space_id]_[user_id]">
<div class="col-md-6 col-lg-3 order-2 mb-4">[user_name]</div>
<div class="col-md-6 col-lg-3 order-2 mb-4">[user_email]</div>
<div class="col-md-6 col-lg-3 order-2 mb-4">
<select class="form-select" style="width: 100pt;" id="sel_role_[space_id]_[user_id]" onchange="changedRole([space_id],[user_id]);">
<option value="0" [selected_0]>[label_normal_user]</option>
<option value="1" [selected_1]>[label_manager]</option>
</select>
</div>
<div class="col-md-6 col-lg-3 order-2 mb-4">
<button class="btn btn-danger" style="word-wrap: unset;" onclick="unspaceUser([space_id],[user_id]);">[label_remove]</button>
</div>
</div>
</div>
<div id="list_user_template_user" style="display: none;">
<div class="row">
<div class="col-md-6 col-lg-3 order-2 mb-4">[user_name]</div>
<div class="col-md-6 col-lg-3 order-2 mb-4">[user_email]</div>
<div class="col-md-12 col-lg-6 order-2 mb-4"></div>
</div>
</div>
<div id="list_space_template" style="display: none;">
<div class="col-md-12 col-lg-12 order-2 mb-4">
<div class="card h-100">
<div class="card-header d-flex align-items-center justify-content-between">
<div class="navbar-nav align-items-center"><p class="fw-semibold d-block">[space_name]</p></div>
</div>
<div class="card-body">
[user_list]
</div>
</div>
</div>
</div>
<div class="row" id="users_list">
</div>
</div>
<div id="spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<div class="card text-center" style="display: none;position: fixed; top: 10px; left: auto; width: 50%; z-index: 9999;"
id="popup_info">
<div class="card-body">
<h5 class="card-title" id="popup_title">Title</h5>
<p class="card-text" id="popup_msg">Msg</p>
<a href="javascript:$('#popup_info').hide();" class="btn btn-primary">OK</a>
</div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered by OpenAI-4o)</a>
</div>
<div class="mb-2 mb-md-0">
<a href="payinf.html" target="_blank" class="footer-link">特定商取引法に基づく表記</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- Core JS -->
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<!-- endbuild -->
<!-- Page JS -->
<script src="files/dashboards-analytics.js"></script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

503
views/payinf.html Normal file
View File

@@ -0,0 +1,503 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>SisAi World</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<script src="files/jquery.js"></script>
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/ua-parser.js"></script>
<script src="files/amipro_utils.js?v=20230501"></script>
<script src="files/portal.js"></script>
<script>
lang_map = new Map();
lang_map.set("en-US", "販売業社の名称");
i18n_messages.set("btn_doc1", lang_map);
lang_map = new Map();
lang_map.set("en-US", "amiPro合同会社");
i18n_messages.set("info_doc1", lang_map);
lang_map = new Map();
lang_map.set("en-US", "所在地");
i18n_messages.set("btn_doc2", lang_map);
lang_map = new Map();
lang_map.set("en-US", "〒150- 東京都渋谷区神宮前六丁目23番4号桑野ビル2階");
i18n_messages.set("info_doc2", lang_map);
lang_map = new Map();
lang_map.set("en-US", "電話番号");
i18n_messages.set("btn_doc3", lang_map);
lang_map = new Map();
lang_map.set("en-US", "03-6219-6055");
i18n_messages.set("info_doc3", lang_map);
lang_map = new Map();
lang_map.set("en-US", "メールアドレス");
i18n_messages.set("btn_doc4", lang_map);
lang_map = new Map();
lang_map.set("en-US", "sales@amipro.me");
i18n_messages.set("info_doc4", lang_map);
lang_map = new Map();
lang_map.set("en-US", "運営統括責任者");
i18n_messages.set("btn_doc5", lang_map);
lang_map = new Map();
lang_map.set("en-US", "杜 慶捷");
i18n_messages.set("info_doc5", lang_map);
lang_map = new Map();
lang_map.set("en-US", "追加手数料等の追加料金");
i18n_messages.set("btn_doc7", lang_map);
lang_map = new Map();
lang_map.set("en-US", "なし");
i18n_messages.set("info_doc7", lang_map);
lang_map = new Map();
lang_map.set("en-US", "交換および返品(返金ポリシー)");
i18n_messages.set("btn_doc8", lang_map);
lang_map = new Map();
lang_map.set("en-US", "商品の特性上、返品・交換はお受けしておりません。");
i18n_messages.set("info_doc8", lang_map);
lang_map = new Map();
lang_map.set("en-US", "引渡時期");
i18n_messages.set("btn_doc9", lang_map);
lang_map = new Map();
lang_map.set("en-US", "注文後すぐにご利用いただけます。");
i18n_messages.set("info_doc9", lang_map);
lang_map = new Map();
lang_map.set("en-US", "受け付け可能な決済手段");
i18n_messages.set("btn_doc9", lang_map);
lang_map = new Map();
lang_map.set("en-US", "クレジットカードまたは国内の銀行振込");
i18n_messages.set("info_doc9", lang_map);
lang_map = new Map();
lang_map.set("en-US", "決済期間");
i18n_messages.set("btn_doc10", lang_map);
lang_map = new Map();
lang_map.set("en-US", "クレジットカード決済の場合はただちに処理されますが、国内の銀行振込の場合は注文から 3 日以内にお振り込みいただく必要があります。");
i18n_messages.set("info_doc10", lang_map);
lang_map = new Map();
lang_map.set("en-US", "販売価格");
i18n_messages.set("btn_doc11", lang_map);
lang_map = new Map();
lang_map.set("en-US", "¥100 - ¥10,000");
i18n_messages.set("info_doc11", lang_map);
$(async function(){
//await set_i18n_messages();
if(navigator.language.toLocaleLowerCase().indexOf("ja")>=0){
document.getElementById("sisai_about_img").src = "files/sisai_about_ja.jpg";
}else if(navigator.language.toLocaleLowerCase().indexOf("cn")>=0){
document.getElementById("sisai_about_img").src = "files/sisai_about_cn.jpg";
}else document.getElementById("sisai_about_img").src = "files/sisai_about_en.jpg";
});
function clickedSisAiAbout(){
if(navigator.language.toLocaleLowerCase().indexOf("ja")>=0){
window.open("files/SisAi-About-ja.pdf", "_blank");
}else if(navigator.language.toLocaleLowerCase().indexOf("cn")>=0){
window.open("files/SisAi-About-cn.pdf", "_blank");
}else window.open("files/SisAi-About-en.pdf", "_blank");
}
</script>
</head>
<body>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="top.html" class="app-brand-link">
<span class="app-brand-logo demo">
<img src="files/favicon.ico" alt="SisAi" width="45%" style="display: block; margin: 10%;" />
</span>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<li class="menu-item">
<a href="top.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics" id="menu_conversation">Conversation</div>
</a>
</li>
<li class="menu-item">
<a href="settings.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div data-i18n="Analytics" id="menu_settings">Settings</div>
</a>
</li>
<li class="menu-item">
<a href="market.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-store-alt"></i>
<div data-i18n="Analytics" id="menu_market">market</div>
</a>
</li>
<li class="menu-item">
<a href="builder.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-wrench"></i>
<div data-i18n="Analytics" id="menu_builder">Space Builder</div>
</a>
</li>
<li class="menu-item">
<a href="mgr.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-user"></i>
<div data-i18n="Analytics" id="menu_mgr">Managers</div>
</a>
</li>
<li class="menu-item">
<a href="docs.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-file"></i>
<div data-i18n="Analytics" id="menu_docs">Documents</div>
</a>
</li>
</ul>
</aside>
<!-- / Menu -->
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
特定商取引法に基づく表記
</div>
</div>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="javascript:void(0);" data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block" id="user_id">A manager</span>
<!--small class="text-muted">Admin<small-->
</div>
</div>
</a>
</li>
<!-- li>
<div class="dropdown-divider"></div>
</>
<li>
<a class="dropdown-item" href="devices.html">
<i class="bx bx-mobile-alt me-2"></i>
<span class="align-middle" id="label_device_mng">Device management</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<span class="d-flex align-items-center align-middle">
<i class="flex-shrink-0 bx bx-credit-card me-2"></i>
<span class="flex-grow-1 align-middle">Billing</span>
<span class="flex-shrink-0 badge badge-center rounded-pill bg-danger w-px-20 h-px-20">!</span>
</span>
</a>
</li -->
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="login.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle" id="label_logout">Log Out</span>
</a>
</li>
</ul>
</li>
<!--/ User -->
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
<div class="accordion mt-3">
<div class="card accordion-item">
<h2 class="accordion-header" id="headingOne">
<button id="btn_doc1" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordionOne" aria-expanded="false" aria-controls="accordionOne">
About
</button>
</h2>
<div id="accordionOne" class="accordion-collapse collapse">
<div class="accordion-body" id="info_doc1">
Recovery Guide
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading2">
<button id="btn_doc2" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion2" aria-expanded="false" aria-controls="accordion2">
Use Guide
</button>
</h2>
<div id="accordion2" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc2">
Use Guide
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading3">
<button id="btn_doc3" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion3" aria-expanded="false" aria-controls="accordion3">
Builder
</button>
</h2>
<div id="accordion3" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc3">
Builder
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading4">
<button id="btn_doc4" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion4" aria-expanded="false" aria-controls="accordion4">
invitation users
</button>
</h2>
<div id="accordion4" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc4">
invitation users
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading5">
<button id="btn_doc5" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion5" aria-expanded="false" aria-controls="accordion5">
invitation users
</button>
</h2>
<div id="accordion5" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc5">
invitation users
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading7">
<button id="btn_doc7" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion7" aria-expanded="false" aria-controls="accordion7">
invitation users
</button>
</h2>
<div id="accordion7" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc7">
invitation users
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading8">
<button id="btn_doc8" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion8" aria-expanded="false" aria-controls="accordion8">
invitation users
</button>
</h2>
<div id="accordion8" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc8">
invitation users
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading9">
<button id="btn_doc9" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion9" aria-expanded="false" aria-controls="accordion9">
invitation users
</button>
</h2>
<div id="accordion9" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc9">
invitation users
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading10">
<button id="btn_doc10" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion10" aria-expanded="false" aria-controls="accordion10">
invitation users
</button>
</h2>
<div id="accordion10" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc10">
invitation users
</div>
</div>
</div>
<div class="card accordion-item">
<h2 class="accordion-header" id="heading11">
<button id="btn_doc11" type="button" class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#accordion11" aria-expanded="false" aria-controls="accordion11">
invitation users
</button>
</h2>
<div id="accordion11" class="accordion-collapse collapse" style="">
<div class="accordion-body" id="info_doc11">
invitation users
</div>
</div>
</div>
</div><!-- end info -->
</div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered by OpenAI-4o)</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- Core JS -->
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<!-- endbuild -->
<!-- Page JS -->
<script src="files/dashboards-analytics.js"></script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

703
views/settings.html Normal file
View File

@@ -0,0 +1,703 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>SisAi World</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<script src="files/jquery.js"></script>
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/ua-parser.js"></script>
<link rel="stylesheet" href="files/spinner.css" />
<script src="files/amipro_utils.js?v=20240620-1"></script>
<script src="files/portal.js"></script>
<script>
lang_map = new Map();
lang_map.set("en-US", "Save");
lang_map.set("zh-CN", "保 存");
lang_map.set("ja", "保 存");
i18n_messages.set("btn_update", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Do you want to leave this page without saving?");
lang_map.set("zh-CN", "您确定要离开此页面而不保存吗?");
lang_map.set("ja", "保存せずにこのページを離れますか?");
i18n_messages.set("msg_saving_alert", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "您确定要切换空间而不保存吗?");
lang_map.set("ja", "保存せずにスペースを切り替えますか?");
lang_map.set("en-US", "Do you want to switch space without saving?");
i18n_messages.set("msg_spc_saving_alert", lang_map);
lang_map = new Map();
lang_map.set("en-US", "The maximum length of the text is 5000 characters.");
lang_map.set("zh-CN", "文本最大长度为5000字。");
lang_map.set("ja", "テキストの最大長は5000文字です。");
i18n_messages.set("label_info_length", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Text is too long.")
lang_map.set("zh-CN", "文本超过最大允许长度。");
lang_map.set("ja", "テキストが長すぎます。");
i18n_messages.set("msg_toolong_text", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "确认删除以下空间吗?");
lang_map.set("en-US", "Do you want to delete the following space?");
lang_map.set("ja", "以下のスペースを削除しますか");
i18n_messages.set("msg_del_alert", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "删除");
lang_map.set("en-US", "Delete");
lang_map.set("ja", "削除");
i18n_messages.set("btn_space_del", lang_map);
lang_map = new Map();
lang_map.set("zh-CN", "重命名");
lang_map.set("en-US", "Rename");
lang_map.set("ja", "名前変更");
i18n_messages.set("btn_space_rename", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Do you want to switch options without saving?");
lang_map.set("zh-CN", "您确定要切换选项而不保存吗?");
lang_map.set("ja", "保存せずにオプションを切り替えますか?");
i18n_messages.set("msg_opt_saving_alert", lang_map);
lang_map = new Map();
lang_map.set("en-US", "The name has been used. Please input another one.");
lang_map.set("zh-CN", "名称已被使用,请输入其他名称");
lang_map.set("ja", "名前が使用されています。別の名前を入力してください。");
i18n_messages.set("msg_same_name", lang_map);
var cur_info_index = -1, cur_space_index = -1;
$(async function(){
$("#user_nickname").text(sessionStorage.getItem("nickname")?sessionStorage.getItem("nickname"):"");
showSpinner();
var url = new URL(window.location.href);
var space_id = url.searchParams.get("sid");
await listSpaces(space_id);
if(document.getElementById("sel_space").childElementCount == 0){
window.location.href = "market.html";
return;
}
if(space_id)await loadSpace(space_id);
else{
var sel_space = document.getElementById("sel_space");
if(sel_space.options.length > 0){
var space_id = sel_space.options[0].value;
await loadSpace(space_id);
}
}
hideSpinner();
});
async function listSpaces(sel_space_id){
try{
var response = await fetch("/listspacenms", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
}
});
if (response.status == 200) {
const data = await response.json();
var sel_space = document.getElementById("sel_space");
for (var i = 0; i < data.spaces.length; i++) {
var opt = document.createElement("option");
if (sel_space_id && sel_space_id == data.spaces[i].space_id) {
opt.selected = true;
cur_info_index = 0;
}
opt.value = data.spaces[i].space_id;
opt.innerHTML = getI18NJsonText(data.spaces[i].space_name);
sel_space.appendChild(opt);
}
} else if (response.status == 440) {
window.location.href = "/login.html";
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
function changedSpace(){
if(document.getElementById("btn_update").disabled == false){
if( confirm( getI18NText(i18n_messages, "msg_spc_saving_alert") ) == false ){
document.getElementById("sel_space").options[cur_space_index].selected = true;
return;
}
}
document.getElementById("btn_update").disabled = true;
var sel_space = document.getElementById("sel_space");
var space_id = sel_space.options[sel_space.selectedIndex].value;
cur_space_index = sel_space.selectedIndex;
loadSpace(space_id);
}
var info_blocks = [];
async function changedInfo(){
if(!info_blocks)return;
if(document.getElementById("btn_update").disabled == false){
if( confirm( getI18NText(i18n_messages, "msg_opt_saving_alert") ) == false ){
document.getElementById("sel_info").options[cur_info_index].selected = true;
return;
}
}
document.getElementById("btn_update").disabled = true;
var sel_space = document.getElementById("sel_space");
var space_id = sel_space.options[sel_space.selectedIndex].value;
var sel_info = document.getElementById("sel_info");
var info_id = sel_info.options[sel_info.selectedIndex].value;
cur_info_index = sel_info.selectedIndex;
var info_textarea = document.getElementById("info_textarea");
//info_textarea.innerHTML = "";
info_textarea.value = "";
try{
const response = await fetch("/getspcinf", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({space_id: space_id, info_id: info_id, language: window.navigator.language})
});
if(response.status == 200){
const data = await response.json();
if(data && data.info){
info_textarea.value = data.info;
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
if(info_blocks){
info_blocks.forEach(function(info){
if(info.id == info_id){
info_textarea.placeholder = info.cmmt?info.cmmt:"";
}
});
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
async function loadSpace(space_id){
var lstJson = {};
lstJson["space_id"] = space_id;
lstJson["language"] = window.navigator.language;
try{
var response = await fetch("/getspace", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(lstJson)
});
if(response.status == 200){
const json = await response.json();
if(json.status == "OK"){
var sel_info = document.getElementById("sel_info");
sel_info.innerHTML = "";
info_blocks = json.blocks;
for(var i = 0; info_blocks && i < info_blocks.length; i++){
var opt = document.createElement("option");
opt.value = info_blocks[i].id;
opt.innerHTML = getI18NJsonText(info_blocks[i].name);
sel_info.appendChild(opt);
}
await changedInfo();
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
function editedTextarea(){
var btn_update = document.getElementById("btn_update");
btn_update.disabled = false;
}
function changingPage(){
var btn_update = document.getElementById("btn_update");
if(btn_update.disabled == false){
return confirm( getI18NText(i18n_messages, "msg_saving_alert") );
}
}
function checkTextLength(){
var info_textarea = document.getElementById("info_textarea");
var label_info_length = document.getElementById("label_info_length");
if(info_textarea.value.length > 5000){
alert(getI18NText(i18n_messages, "msg_toolong_text"));
return false;
}
return true;
}
function updateInfo(){
if(!checkTextLength())return;
showSpinner();
var sel_space = document.getElementById("sel_space");
var space_id = sel_space.options[sel_space.selectedIndex].value;
var sel_info = document.getElementById("sel_info");
var info_id = sel_info.options[sel_info.selectedIndex].value;
var info_textarea = document.getElementById("info_textarea");
var lstJson = {};
lstJson["space_id"] = space_id;
lstJson["info_id"] = info_id;
lstJson["info"] = info_textarea.value;
lstJson["language"] = window.navigator.language;
fetch("/setspcinf", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(lstJson)
}).then(function(response){
hideSpinner();
if(response.status == 200){
response.json().then(function(data){
if(data.status == "OK"){
var btn_update = document.getElementById("btn_update");
btn_update.disabled = true;
}else{
window.location.href = "/login.html";
}
});
}else if (response.status == 440) {
window.location.href = "/login.html";
}
});
}
async function delSpace(){
var sel_space = document.getElementById("sel_space");
if(sel_space.options.length == 0)return;
if( confirm( getI18NText(i18n_messages, "msg_del_alert")+"\n"+ sel_space.options[sel_space.selectedIndex].innerHTML) == false ) return;
var space_id = sel_space.options[sel_space.selectedIndex].value;
var lstJson = {};
lstJson["space_id"] = space_id;
try{
var response = await fetch("/delspc", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(lstJson)
});
if(response.status == 200){
const data = await response.json();
if(data.status == "OK"){
await listSpaces();
var sel_space = document.getElementById("sel_space");
if(sel_space.options.length > 0){
var space_id = sel_space.options[0].value;
await loadSpace(space_id);
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
async function renameSpace(){
var sel_space = document.getElementById("sel_space");
if(sel_space.options.length == 0)return;
var space_id = sel_space.options[sel_space.selectedIndex].value;
var space_name = sel_space.options[sel_space.selectedIndex].text;
var new_name = prompt(getI18NText(i18n_messages, "btn_space_rename"), space_name);
if(new_name == null || new_name == "")return;
try{
var response = await fetch("/chksamenm", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
type: 'space',
space_id: '--create--',
space_name: new_name
}),
});
if(response.status == 200){
const resp = await response.json();
if(resp.status == "OK"){
if(resp.same == true){
alert(getI18NText(i18n_messages, 'msg_same_name'));
return;
}
}else window.location.href = "/login.html";
}else {
window.location.href = "/login.html";
}
var lstJson = {};
lstJson["space_id"] = space_id;
lstJson["space_name"] = new_name;
lstJson["language"] = window.navigator.language;
var response = await fetch("/renmspc", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(lstJson)
});
if(response.status == 200){
const data = await response.json();
if(data.status == "OK"){
sel_space.options[sel_space.selectedIndex].text = new_name;
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}else if (response.status == 440) {
window.location.href = "/login.html";
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_err'));
}finally{
hideSpinner();
}
}
function showSpinner() {
document.getElementById("spinner").style.display = "block";
}
function hideSpinner() {
document.getElementById("spinner").style.display = "none";
}
</script>
</head>
<body>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand demo">
<a href="top.html" class="app-brand-link">
<span class="app-brand-logo demo">
<img src="files/favicon.ico" alt="SisAi" width="45%" style="display: block; margin: 10%;" />
</span>
</a>
</div>
<div class="menu-inner-shadow"></div>
<ul class="menu-inner py-1">
<li class="menu-item">
<a href="top.html" class="menu-link" onclick="return changingPage();">
<i class="menu-icon tf-icons bx bx-home-circle"></i>
<div data-i18n="Analytics" id="menu_conversation">Conversation</div>
</a>
</li>
<li class="menu-item active">
<a href="settings.html" class="menu-link" onclick="javascript:return false;">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div data-i18n="Analytics" id="menu_settings">Settings</div>
</a>
</li>
<li class="menu-item">
<a href="market.html" class="menu-link" onclick="return changingPage();">
<i class="menu-icon tf-icons bx bx-store-alt"></i>
<div data-i18n="Analytics" id="menu_market">market</div>
</a>
</li>
<li class="menu-item">
<a href="builder.html" class="menu-link">
<i class="menu-icon tf-icons bx bx-wrench"></i>
<div data-i18n="Analytics" id="menu_builder">Space Builder</div>
</a>
</li>
<li class="menu-item">
<a href="mgr.html" class="menu-link" onclick="return chnagingPage();">
<i class="menu-icon tf-icons bx bx-user"></i>
<div data-i18n="Analytics" id="menu_mgr">Managers</div>
</a>
</li>
<li class="menu-item">
<a href="docs.html" class="menu-link" onclick="return chnagingPage();">
<i class="menu-icon tf-icons bx bx-file"></i>
<div data-i18n="Analytics" id="menu_docs">Documents</div>
</a>
</li>
</ul>
</aside>
<!-- / Menu -->
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
<select class="form-select" id="sel_space" style="min-width: 10pt;" aria-label="selected space" onchange="changedSpace();">
</select>
</div>
</div>
<button style="width:20%;float: right;white-space:nowrap;margin-left: 5pt;padding:0.4rem 0;" class="btn btn-danger" onclick="javascript:delSpace();" id="btn_space_del">Delete</button>
<button style="width:20%;float: right;white-space:nowrap;margin-left: 5pt;padding:0.4rem 0;" class="btn btn-primary" onclick="javascript:renameSpace();" id="btn_space_rename">Rename</button>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow" href="javascript:void(0);" data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#">
<div class="d-flex">
<div class="flex-shrink-0 me-3">
<div class="avatar avatar-online">
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<span class="fw-semibold d-block" id="user_nickname">A manager</span>
<!-- small class="text-muted">Admin</ -->
</div>
</div>
</a>
</li>
<!-- li>
<div class="dropdown-divider"></div>
</>
<li>
<a class="dropdown-item" href="devices.html">
<i class="bx bx-mobile-alt me-2"></i>
<span class="align-middle" id="label_device_mng">Device management</span>
</a>
</li>
<li>
<a class="dropdown-item" href="#">
<span class="d-flex align-items-center align-middle">
<i class="flex-shrink-0 bx bx-credit-card me-2"></i>
<span class="flex-grow-1 align-middle">Billing</span>
<span class="flex-shrink-0 badge badge-center rounded-pill bg-danger w-px-20 h-px-20">!</span>
</span>
</a>
</li -->
<li>
<div class="dropdown-divider"></div>
</li>
<li>
<a class="dropdown-item" href="login.html">
<i class="bx bx-power-off me-2"></i>
<span class="align-middle" id="label_logout">Log Out</span>
</a>
</li>
</ul>
</li>
<!--/ User -->
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row">
<div class="col-md-12 col-lg-12 order-2 mb-4">
<div class="card h-100">
<div class="card-header d-flex align-items-center justify-content-between">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
<select class="form-select" id="sel_info" aria-label="selected info" onchange="changedInfo();">
</select>
</div>
</div>
<button style="width:20%;float: right;white-space:nowrap;padding: 0.4rem 0;" class="btn btn-info" disabled="1" onclick="javascript:updateInfo();" id="btn_update">Update</button>
</div>
<div class="card-body">
<textarea class="form-control" style="width: 96%;height: 200pt;" placeholder="" id="info_textarea" oninput="javascript:editedTextarea();">
</textarea>
<p class="text-muted" style="margin-top: 10px;" id="label_info_length"></p>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered by OpenAI-4o)</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- Core JS -->
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<!-- endbuild -->
<!-- Vendors JS -->
<!-- script src="files/apexcharts.js">< -->
<!-- Page JS -->
<script src="files/dashboards-analytics.js"></script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>

4
views/test.html Normal file
View File

@@ -0,0 +1,4 @@
<html>
<head>
<title>Test</title>
</html>

1146
views/top.html Normal file

File diff suppressed because it is too large Load Diff

675
views/user.html Normal file
View File

@@ -0,0 +1,675 @@
<!DOCTYPE html>
<html
lang="en-US"
class="light-style"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<title>User info - SisAi world</title>
<meta name="description" id="site_desc" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
rel="stylesheet"
/>
<!-- Icons. Uncomment required icon fonts -->
<link rel="stylesheet" href="files/boxicons.css?v=20230405" />
<!-- Core CSS -->
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="files/demo.css" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
<!-- Page CSS -->
<!-- Helpers -->
<script src="files/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="files/config.js"></script>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script>
<script src="files/menu.js"></script>
<script src="files/main.js"></script>
<script src="files/ua-parser.js"></script>
<link rel="stylesheet" href="files/spinner.css" />
<script src="files/amipro_utils.js?v=20230401402"></script>
<script>
var user_id;
const i18n_messages = new Map();
var lang_map = new Map();
lang_map.set("en-US", "Add device");
lang_map.set("zh-CN", "添加设备");
lang_map.set("ja", "デバイスを追加");
i18n_messages.set("btn_add", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Go to top page");
lang_map.set("zh-CN", "去首页");
lang_map.set("ja", "トップページへ");
i18n_messages.set("btn_top", lang_map);
lang_map = new Map();
lang_map.set("en-US", "My devices");
lang_map.set("zh-CN", "我的设备");
lang_map.set("ja", "マイデバイス");
i18n_messages.set("my_devices", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device registered");
lang_map.set("zh-CN", "添加设备成功");
lang_map.set("ja", "デバイス登録完了。");
i18n_messages.set("msg_register_ok", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device");
lang_map.set("zh-CN", "设备");
lang_map.set("ja", "デバイス");
i18n_messages.set("title_device", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Registered time");
lang_map.set("zh-CN", "添加时间");
lang_map.set("ja", "登録時間");
i18n_messages.set("title_time", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Actions");
lang_map.set("zh-CN", "操作");
lang_map.set("ja", "操作");
i18n_messages.set("title_act", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Delete");
lang_map.set("zh-CN", "删除");
lang_map.set("ja", "削除");
i18n_messages.set("title_del", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Contact us");
lang_map.set("zh-CN", "联系我们");
lang_map.set("ja", "お問い合わせ");
i18n_messages.set("title_contact", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Log out");
lang_map.set("zh-CN", "登出");
lang_map.set("ja", "ログアウト");
i18n_messages.set("title_logout", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Deleted device.");
lang_map.set("zh-CN", "设备删除成功。");
lang_map.set("ja", "デバイスを削除しました。");
i18n_messages.set("msg_deldev_ok", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Do you want to delete this device?");
lang_map.set("zh-CN", "确认删除此设备吗?");
lang_map.set("ja", "デバイスを削除しますか?");
i18n_messages.set("msg_confirm_deldev", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Account information");
lang_map.set("zh-CN", "账号信息");
lang_map.set("ja", "アカウント情報");
i18n_messages.set("user_info", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Save");
lang_map.set("zh-CN", "保 存");
lang_map.set("ja", "保 存");
i18n_messages.set("btn_update_info", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Nick name");
lang_map.set("zh-CN", "昵 称");
lang_map.set("ja", "ニックネーム");
i18n_messages.set("label_nick_name", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password");
lang_map.set("zh-CN", "密 码");
lang_map.set("ja", "パスワード");
i18n_messages.set("label_password", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password again");
lang_map.set("zh-CN", "确认密码");
lang_map.set("ja", "パスワード(確認)");
i18n_messages.set("label_pw_confirm", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password and confirmation password are different. Please re-enter.");
lang_map.set("zh-CN", "密码与确认密码不同,请重新输入。");
lang_map.set("ja", "パスワードが一致しません。もう一度入力してください。");
i18n_messages.set("msg_pw_confirm_err", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Please enter a nickname. Enter between 5 and 30 characters.");
lang_map.set("zh-CN", "请输入昵称长度在5-30字之间。");
lang_map.set("ja", "ニックネームを入力してください。5-30文字の間で入力してください。");
i18n_messages.set("msg_input_nickname", lang_map);
lang_map = new Map();
lang_map.set("en-US", "This nickname is already in use. Please enter another nickname");
lang_map.set("zh-CN", "此昵称已经被使用,请重新输入。");
lang_map.set("ja", "このニックネームは既に使用されています。別のニックネームを入力してください。");
i18n_messages.set("msg_using_nickname", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Password must be at least 8 characters long.");
lang_map.set("zh-CN", "请输入密码长度大于8个文字。");
lang_map.set("ja", "パスワードは8文字以上で入力してください。");
i18n_messages.set("msg_pw_short_err", lang_map);
lang_map = new Map();
lang_map.set("en-US", "System error. Please try later.");
lang_map.set("zh-CN", "系统错误,请稍后再试。");
lang_map.set("ja", "システムエラー。後でもう一度お試しください。");
i18n_messages.set("msg_sys_fail", lang_map);
var reg_session_id = null;
var invite_session_id = null;
window.onload = async function() {
setI18NText(i18n_messages);
let url = new URL(window.location.href);
let params = url.searchParams;
reg_session_id = params.get("rid");
invite_session_id = params.get("nid");
//showSpinner();
if(reg_session_id){
const reg_username = await getRegistrationUser(reg_session_id);
user_id = reg_username;
if(reg_username && 0 < reg_username.length){
$('#user_email').html(user_id);
}else{
window.location.href = "login.html";
}
}else if(invite_session_id){
const response = await fetch("/invtinguser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({session: invite_session_id})
});
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
if(resp.exist){
processInviteSession(invite_session_id)
window.location.href = "top.html";
}else{
//Register user first
$('#user_email').html(resp.user_email);
}
}else{//No session
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
window.location.href = "login.html";
}
}
}
hideSpinner();
}
async function getRegistrationUser(reg_session_id){
showSpinner();
var lstJson = {};
lstJson["session"] = reg_session_id;
try{
const response = await fetch("/getreguser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
hideSpinner();
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
return resp.user
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}finally{
hideSpinner();
}
return null;
}
async function updateUser(){
if($('#password').val().length < 8){
alert(getI18NText(i18n_messages, 'msg_pw_short_err'));
//alert("Password must be at least 8 characters long.");
return;
}
if($('#password').val() != $('#pw_confirm').val()){
alert(getI18NText(i18n_messages, 'msg_pw_confirm_err'));
return;
}
var user_name = $('#user_name').val().trim();
if(user_name.length < 5 || user_name.length > 30){
alert(getI18NText(i18n_messages, 'msg_input_nickname'));
return;
}
showSpinner();
//add try-catch for await rejection
try{
const result = await fetch("/chksamenm", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({type:'nick', nickname: user_name})
});
if(result.status == 200){
const resp = await result.json();
if (resp.status=='OK') {
if(resp.same){
hideSpinner();
alert(getI18NText(i18n_messages, 'msg_using_nickname'));
return;
}
}
}else{
hideSpinner();
return;
}
var lstJson = {};
if(reg_session_id && 0<reg_session_id.length) lstJson["reg_session_id"] = reg_session_id;
else if(invite_session_id && 0<invite_session_id.length) lstJson["invt_session_id"] = invite_session_id;
lstJson["user_name"] = user_name;
lstJson["password"] = $('#password').val();
lstJson["language"] = window.navigator.language;
const response = await fetch("/setuser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
if(response.status == 200){
const email = $('#user_email').html();
const resp = await response.json();
if (resp.status=='OK') {
if(reg_session_id){
login(email, lstJson["password"],"market.html");
}else if(invite_session_id && 0 < invite_session_id.length){
processInviteSession(invite_session_id)
login(email, lstJson["password"], "top.html");
}else{
window.location.href = "top.html";
}
}else{
alert(resp.errorMessage);
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}finally{
hideSpinner();
}
}
async function login(email, pwd, goto){
const response = await fetch("/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({email: email, pwd: pwd}),
cache: "no-cache"
})
if(response.status == 200){
const resp = await response.json();
if (resp.status!='OK') {
window.location.href = "top.html";
}else{
const nm = getI18NJsonText(resp.nickname);
sessionStorage.setItem('nickname', nm);
window.location.href = goto;
}
}
}
async function processInviteSession(invite_session_id){
showSpinner();
var lstJson = {};
lstJson["session"] = invite_session_id;
try{
const response = await fetch("/invt", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(lstJson)
})
hideSpinner();
if(response.status == 200){
const resp = await response.json();
if (resp.status=='OK') {
return resp.user_email
}
}
}catch(err){
alert(getI18NText(i18n_messages, 'msg_sys_fail'));
}finally{
hideSpinner();
}
return null;
}
async function registerUser(user_email) {
try {
let req = {user_email: user_email, session_id: sessionStorage.getItem("session_id")}
const response = await fetch("/reguser", {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(req)
})
} catch (err) {
console.log(err)
return null;
}finally{
hideSpinner();
}
}
async function addDevice(){
const the_user_id = user_id?user_id:sessionStorage.getItem("uid")
const result = await registerFido2(the_user_id, 'user_'+the_user_id);
var rtn = false
if(result.status === 'ok'){
if(reg_session_id){
await registerUser(the_user_id);
}
alert($('#msg_register_ok').html());
rtn = true;
}else{
errProcessFido2(result)
rtn = false;
}
listDevices();
return rtn
}
async function delDevice(device_id){
if(window.confirm(getI18NText(i18n_messages, 'msg_confirm_deldev')) == true){
const result = await delUserDeviceFido2(device_id)
if("ok" === result.status){
alert(getI18NText(i18n_messages, 'msg_deldev_ok'));
listDevices();
}else{
const msg = getI18NErrorMessage(result.errorMessage);
alert(msg?msg:result.errorMessage);
}
}
}
async function listDevices(){
const result = await listUserDevicesFido2()
$("#devices_list").html("");
if("ok" === result.status){
let lst_html = ""
for(let dev of result.devices){
var dev_desc = dev.desc
if(!dev_desc || 0 === dev_desc.length){
let parser = new UAParser(dev.userAgent);
if(parser.getOS().name){
dev_desc = parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name;
}else{
dev_desc = dvc.userAgent;
}
}
var date = new Date(dev.registered_time);
lst_html += '<tr><td><strong>'+ dev_desc +'</strong></td><td>'+
date.toLocaleString()+'</td><td>'+
"<a style=\"padding-left: 0px;\" class=\"dropdown-item\" href=\"javascript:delDevice('"+dev.device_id+"');\">"+
'<i class="bx bx-trash me-1"></i><span id="title_del"> '+ getI18NText(i18n_messages, 'title_del') +'</span></a></div></td></tr>'
}
$("#devices_list").html(lst_html);
}else{
const msg = getI18NErrorMessage(result.errorMessage); //fido2LibErrMsgLanguages.japanese
alert(msg?msg:result.errorMessage);
}
}
function showSpinner() {
$('#btn_add').prop('disabled', true);
$('#btn_top').prop('disabled', true);
document.getElementById("spinner").style.display = "block";
}
function hideSpinner() {
$('#btn_add').prop('disabled', false);
$('#btn_top').prop('disabled', false);
document.getElementById("spinner").style.display = "none";
}
</script>
</head>
<body>
<div id="msg_register_ok" style="display:none;">Device registered</div>
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Layout container -->
<div class="layout-page">
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
<i class="bx bx-menu bx-sm"></i>
</a>
</div>
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
<i class="bx bx-user fs-4 lh-0"></i>
<span id="user_email"></span>
</div>
</div>
<ul class="navbar-nav flex-row align-items-center ms-auto">
<li>
<button style="display:none;" class="btn btn-info" onclick="javascript:window.location='top.html'"
id="btn_top">Go to top</button>
</li>
</ul>
</div>
</nav>
<!-- / Navbar -->
<!-- Content wrapper -->
<div class="content-wrapper">
<!-- Content -->
<div>
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<h5 class="card-header" id="user_info">My email</h5>
<div class="card-body">
<div class="row justify-content-center">
<div class="col-md-6">
<!-- form class="mb-3" action="javascript:authenticate();" method="POST"></!-->
<div class="mb-3">
<label for="uid" class="form-label" id="label_nick_name">Nick name</label>
<input type="text" class="form-control" id="user_name" name="user_name" placeholder="Enter your nick name" autofocus="">
</div>
<div class="mb-3 form-password-toggle">
<div class="d-flex justify-content-between">
<label class="form-label" for="password" id="label_password">Password</label>
</div>
<div class="input-group input-group-merge">
<input type="password" id="password" class="form-control" name="password" placeholder="············" >
<span class="input-group-text cursor-pointer"></span>
</div>
</div>
<div class="mb-3 form-password-toggle">
<div class="d-flex justify-content-between">
<label class="form-label" for="pw_confirm" id="label_pw_confirm">Password again</label>
</div>
<div class="input-group input-group-merge">
<input type="password" id="pw_confirm" class="form-control" name="pw_confirm" placeholder="············" >
<span class="input-group-text cursor-pointer"></i></span>
</div>
</div>
<div class="mb-3">
<button class="btn btn-primary d-grid w-100" id="btn_update_info" onclick="javascript:updateUser();">Update</button>
</div>
<!-- /form -->
</div>
</div>
</div>
</div>
</div>
<!-- div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<h5 class="card-header" id="my_devices">My devices</h5>
<div style="margin-right: 10px;">
<button class="btn btn-primary" style="width:30%;float: right;margin-left: 66%;" onclick="addDevice();"
id="btn_add">Add device</button>
</div>
<div class="table-responsive text-nowrap">
<table class="table">
<thead>
<tr>
<th id="title_device">Device</th>
<th id="title_time">Registered time</th>
<th id="title_act">Actions</th>
</tr>
</thead>
<tbody class="table-border-bottom-0" id="devices_list">
<tr>
<td colspan="3" style="text-align: center;" id="title_empty_list"></td>
</tr>
</tbody>
</table>
</div>
</div>
</ -->
<div id="spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<!-- / Content -->
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0">
©
<script>
document.write(new Date().getFullYear());
</script>
<a href="#" target="_blank" class="footer-link fw-bolder">amiPro(Powered by OpenAI-4o)</a>
</div>
<div>
<a
href="mailto:support@amipro.me?subject=contact"
target="_blank"
class="footer-link me-4"
id="title_contact"
>Contact</a
>
</div>
</div>
</footer>
<!-- / Footer -->
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- div class="buy-now">
<a
href="https://themeselection.com/products/sneat-bootstrap-html-admin-template/"
target="_blank"
class="btn btn-danger btn-buy-now"
>Upgrade to Pro</a
>
</div -->
<!-- Page JS -->
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>